P1053 [NOIP 2005 提高组] 篝火晚会
链接
https://www.luogu.com.cn/problem/P1053
思路
首先很明显可以按顺序构造一长条,如果不行那么直接返回false就ok。 接下来是这里之前没理解的地方: 之前的想法是找对应的未重合部分 例如对:`1 2 3 4 5`,`1 5 2 4 3`。很显然是3步,因为有三个不重合。 那么换个思路想:对于一个序列的任何一个点,如果能移动k步就能到达原位,说明只要统计k相同的就可以知道一个完整的情况(从这个角度出发,直接统计步数的最大值就行了) 还有一个点:例如有可能有`1 2 3 4 5`,`5 4 3 2 1`的对称情况,所以需要处理对称情况(i对应的对称就是n-i+1)代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define tin long long
#define itn long long
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
const int N = 1e5 + 10;
// n:人数,l/r:希望相邻的两个同学
itn n;
int l[N], r[N];
int initial[N], target[N];
// 记录每个人希望相邻的两个人
int people[N][3];
int pluss[N] = { 0 }, minuss[N] = { 0 };
// 构建目标链
bool build_target() {
target[1] = 1;
target[2] = people[1][2];
for (int i = 2; i <= n - 1; i++) {
int cur = target[i];
int pre = target[i - 1];
// cur的希望相邻的两个人分别是people[cur][1]和people[cur][2]
// pre已经是cur的前一个,那么下一个是谁?
if (people[cur][1] == pre) {
target[i + 1] = people[cur][2];
}
else if (people[cur][2] == pre) {
target[i + 1] = people[cur][1];
}
else {
return false; // 构建失败
}
}
return true;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
people[i][1] = l[i];
people[i][2] = r[i];
}
// 构建目标链
if (!build_target()) {
cout << -1 << endl;
return;
}
// 构建初始链
for (int i = 1; i <= n; i++) {
initial[i] = i;
}
// 顺时针和逆时针统计差值
for (int i = 1; i <= n; i++) {
pluss[(target[i] - initial[i] + n) % n]++;
minuss[(target[i] - initial[n - i + 1] + n) % n]++;
}
int ansmax = 0;
for (int i = 0; i < n; i++) {
ansmax = max(ansmax, max(pluss[i], minuss[i]));
}
cout << n - ansmax << endl;
}
signed main() {
//freopen("D:\\下载\\P1053_8.in", "r", stdin);
IOS;
solve();
return 0;
}