题解 P1053 【篝火晚会】
NOIP2005 提高组——篝火晚会
为获取最佳阅读效果,建议访问https://www.actinoi.com/2019/06/22/NOIP2005提高组——篝火晚会/
本题有一个坑,那就是移动的人不需要连续。然后。。。知道这个坑点之后,我们很容易想到最优解,那就是化环为链,构建目标链与初始链,然后找到目标链与初始链中不一样的人的总数,用总人数
例如,初始环是左边的这个环,目标环是右边的环,如箭头所示的路径,我们可以通过
(2,5,6) 的指令O(N) 完成变换。
但是,环是可以旋转的,因此我们并不知道怎样转动是最优的。我们便可以考虑以每个点为起点进行搜索,因此,一个绝佳的
无疑,这个算法会让我们看到一片
如上图,我们从
初始链: 1, 2, 3, 4, 5, 6
目标链: 1, 6, 3, 4, 2, 5
我们将两条链对应的数相减,取绝对值,便可以得到:
差值:0, 4, 0, 0, 3, 1
在这条差值中,
等等!
这既然是一个环,那么,会不会构建目标链得到的结果不一样呢?的确是这样滴。还是以上图为例,从
那么,什么时候不能符合每个同学的愿望呢?其实,只要构出目标环,便一定可以用玄学的方法使每个同学满意。那么,只有在构不成目标环的时候才输出 (好一个悲伤的故事)的时候才构不成环,此时输出
最后附上代码:
#include <iostream>
using namespace std;
int target[50001], initial[50001], people[50001][3], pluss[50001], minuss[50001]; //存储目标链,初始链,每个人最希望相邻的两个同学的编号,正序相同人数以及逆序相同人数
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch=='-')
w=-1;
ch=getchar();
}
while(ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
int main() {
int n;
n = read();
for (int i = 1; i <= n; i++) //读入编号是 i 的同学最希望相邻的两个同学的编号
people[i][1] =read(), people[i][2] = read();
target[1] = 1;
target[2] = people[1][2]; //构建目标链
initial[1] = 1;
initial[n] = n; //构建初始链
for (int i = 2; i <= n - 1; i++) {
initial[i] = i; //构建初始链
if (target[i - 1] == people[target[i]][1])
target[i + 1] = people[target[i]][2];
else if (target[i - 1] == people[target[i]][2])
target[i + 1] = people[target[i]][1]; //构建目标链
else{
cout << -1 << endl; //第 i 个人希望相邻的人旁边没有空位了,无法构建目标链(一个悲伤的故事)
return 0;
}
}
int ans = 0;
for(int i = 1; i <= n; i++){
pluss[(target[i] - initial[i] + n) % n]++; //顺时针从 1 ~ n 跑一遍
minuss[(target[i]- initial[n - initial[i] + 1] + n) % n]++; //逆时针从 n ~ 1 跑一遍差
}
for (int i = 0; i <= n - 1; i++)
ans = max(ans, max(pluss[i], minuss[i])); //找差值人数最多的
cout << n - ans; //总人数 - 不用移动的人数 = 需要移动的人数,也就是答案
return 0;
}