[SMOJ2083]篝火晚会
来源:互联网 发布:网络购物流程 编辑:程序博客网 时间:2024/04/28 20:34
首先考虑无解的情况。显然,如果目标环存在,则一定有解。否则无解,可以进行一次遍历,如果能够正好经过
接下来,就可以根据所构造的环,考虑花费。来看下面这个例子(为了方便,破成链表示):
原环:1 2 3 4
目标:3 2 4 1
显然,2 已经在目标位置,如果将其移动,肯定会没有必要的额外花费。考虑 1,它要去向 4 的位置,因此
可以发现,每个人到达目标位置的代价都是 1,而且到达之后不再移动,而是把原来所在的那个人“挤走”,让他自己找位置去。一轮重复下来可以发现,其实不在原位人数就是费用。但是要注意题目是一个环,上面只是为了方便表示,破成了一种可能的链。事实上,形如 2 4 1 3 或者 4 1 3 2 也是可能的。
这样就要通过枚举开头破环,扫一遍算花费,取个 min 就行了。需要注意的是,环的方向可以有正反两种,都要考虑。时间复杂度
上面的思路,是直接算“不在位人数”作为花费。如果逆向思考,发现“
例如:(target 是把环展开了,有 4 种可能的开头,最后一个 1 打多了)
简单地推一下就可以发现,记
这样,就可以考虑计算对于最终环中的每个人,将开头放在什么位置可以使其不用移动。最后在所有情况中选择不用移动的人最多的,用
时间复杂度为
这题知道了正解之后看似不难(然而比赛的时候我还是没想出来),不过仔细品味一下,其实里面有一种方法还是很重要的,即通过枚举来破环。往往我们对于一些无法直接解决的限制,可以考虑利用枚举来突破,有时可以取得很好的效果。
参考代码:
#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>using namespace std;const int MAXN = 5e4 + 100;int n;int wishcnt[MAXN], wish1[MAXN][2], wish2[MAXN][2]; //被多少人希望,自己希望谁,谁希望自己bool init() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d%d", &wish1[i][0], &wish1[i][1]); if (wishcnt[wish1[i][0]] == 2 || wishcnt[wish1[i][1]] == 2) return false; //不可能有超过 2 个人同时希望与一个人坐 wish2[wish1[i][0]][wishcnt[wish1[i][0]]++] = wish2[wish1[i][1]][wishcnt[wish1[i][1]]++] = i; for (int j = 0; j < 2; j++) { //自己希望的人是否希望自己 if (wish1[i][j] > i) continue; bool ok = false; for (int k = 0; k < 2; k++) if (wish1[wish1[i][j]][k] == i) ok = true; if (!ok) return false; } for (int j = 0; j < wishcnt[i]; j++) //希望自己的人是否被自己所希望 if (wish2[i][j] != wish1[i][0] && wish2[i][j] != wish1[i][1]) return false; } return true;}int circle[MAXN], start[MAXN][2];bool vis[MAXN];int solve() { if (!init()) return -1; circle[1] = 1; circle[2] = wish1[1][0]; vis[circle[1]] = vis[circle[2]] = true; //circle 为调整后的环 for (int i = 2; i < n; i++) { if (circle[i - 1] == wish1[circle[i]][0]) circle[i + 1] = wish1[circle[i]][1]; else if (circle[i - 1] == wish1[circle[i]][1]) circle[i + 1] = wish1[circle[i]][0]; else return -1; vis[circle[i + 1]] = true; } for (int i = 1; i <= n; i++) if (!vis[i]) return -1; int ans1 = 0, ans2 = 0; for (int i = 1; i <= n; i++) { ans1 = max(ans1, ++start[(circle[i] - i + n) % n][0]); //正 ans2 = max(ans2, ++start[(circle[n - i + 1] - i + n) % n][1]); //反 } return n - max(ans1, ans2);}int main(void) { freopen("2083.in", "r", stdin); freopen("2083.out", "w", stdout); printf("%d\n", solve()); return 0;}
- [SMOJ2083]篝火晚会
- 篝火晚会 题解
- VIJOS 1008 篝火晚会
- 【模拟】篝火晚会(noip2005)
- Vijos1008 篝火晚会
- Codevs 1106 篝火晚会
- NOIP2005 篝火晚会
- NOIP2005篝火晚会
- RQNOJ 篝火晚会
- NOIP2005 篝火晚会
- RQNOJ-19 篝火晚会
- P1053 篝火晚会
- 洛谷 P1053 篝火晚会
- NOIP 2005 篝火晚会
- 篝火晚会(脑洞题)
- 【b503】篝火晚会
- [codevs1106][NOIP2005]篝火晚会
- P1053 篝火晚会
- phpmyadmin设置登录认证
- [NOIP2016] 天天爱跑步 (LCA+线段树(动态开点)+差分+dfs序)
- 基于Bmob从零开始写一个博客小程序
- automake 和 autoconf自动生成makefile 根据编译器进行宏定义
- Python3之面向对象高级__str__用法
- [SMOJ2083]篝火晚会
- Linux--iptables内置防火墙
- xampp无法启动MySQL
- 20170814(三道题-DAG上DP 二分查找 map)
- python中的全局变量和局部变量
- 测试六 赛后感受
- 入门Laravel的笔记
- 递归
- (二分+模拟)GYM_100253 Travelling Camera Problem