题目描述
佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有 �n 个同学,编号从 11 到 �n。一开始,同学们按照 1,2,⋯ ,�1,2,⋯,n 的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。
佳佳可向同学们下达命令,每一个命令的形式如下:
(�1,�2,...��−1,��)(b1,b2,...bm−1,bm)
这里 �m 的值是由佳佳决定的,每次命令 �m 的值都可以不同。这个命令的作用是移动编号是 �1,�2,⋯ ,��b1,b2,⋯,bm 的这 �m 个同学的位置。要求 �1b1 换到 �2b2 的位置上,�2b2 换到 �3b3 的位置上,……,要求 ��bm 换到 �1b1 的位置上。执行每个命令都需要一些代价。我们假定如果一个命令要移动 �m 个人的位置,那么这个命令的代价就是 �m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?
输入格式
第一行是一个整数 �n,表示一共有 �n 个同学。
其后 �n 行每行包括 22 个不同的正整数,以一个空格隔开,分别表示编号是 11 的同学最希望相邻的两个同学的编号,编号是 22 的同学最希望相邻的两个同学的编号,……,编号是�n的同学最希望相邻的两个同学的编号。
输出格式
一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出 −1−1。
输入输出样例
输入 #1复制
4 3 4 4 3 1 2 1 2
输出 #1复制
2
说明/提示
- 对于 30%30% 的数据,满足 �≤1000n≤1000;
- 对于 100%100% 的数据,满足 3≤�≤500003≤n≤50000。
【题目来源】
NOIP 2005 提高组第三题
p.s.非常抱歉之前的题解出现了错误,少判断了最后一位是不是满足要求,现已更正,感谢 @G_keng 同学热心指出并提供hack数据。
题目https://www.luogu.org/problemnew/show/P1053
这个题最大的坑点在于:m个人可以不连续!!!!
~~这个锅题目描述得背,~~描述里b1,b2,...bm实在是太规则了,使我以为操作必须得从1开始,并且要连续...
然后就没那么难了,我们考虑,把环破成链,假设有k个数在当前链和目标链中的位置相同,那么就有n-k个不相同的,我们每次操作只对这n-k个数进行,我们最优可以做到每操作x个数,就一次性把这x个数全放到正确位置上,比如说:
1 2 3 4 5 6 7 8 9 10 -> 1 8 2 7 5 3 6 4 10 9
先通过(10,8,1)-> 8 2 3 4 5 6 7 10 9 1
再通过(3,5,4,7) 就可以了。
(注意是环:8 2 7 5 3 6 4 10 9 1和1 8 2 7 5 3 6 4 10 9是一样的)
这样就做到了每次只操作位置不对的,实现了最小化代价。
所以,结论就是:把初始链变成目标链的最小代价为n-k。(k的意义同上)
那么我们只需要O(n)搞出目标链的两种情况,然后每次右移(左移)之后与1,2,3,...,n作对比,求得最小的n-k。
但这样是O(n^2)的......
我们可以优化:
初始链 | 1 2 3 4 5 6 |
---|---|
目标链 | 4 2 3 1 5 6 |
差值 | 3 0 0 3 0 0 |
差值指的是需要右移的次数
这个例子下,可以发现,不管怎么右移,差值虽变化,但相同的一直相同,不同的一直不同,那么我们就可以只搞出一个目标链,O(n)求一下此时的差值,然后找出其中相同个数最多的就可以了。
具体做法就是:对于所给的数据,我们可以通过O(n)建一条链,如果建不出来,输出-1。建出来之后,与1,2,3...,n和n,n-1,...2,1分别求一遍差值,然后统计最大的,然后,完了。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int Maxn=50010;
int Dv1[Maxn],Dv2[Maxn];
//分别表示与1,2,...,n和n,n-1,...,2,1的差值
int vis[Maxn];
int c[Maxn];//目标链
int l1[Maxn],l2[Maxn];
int si=1,n,ans=0;
inline int read()//读入优化
{
int fl=1,rt=0; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') fl=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){rt=rt*10+ch-'0'; ch=getchar();}
return fl*rt;
}
void Build()//建目标链
{
c[1]=1; c[2]=l1[1]; vis[c[1]]=vis[c[2]]=1;
for(int i=2;i<=n-1;i++)
{
if(c[i-1]==l1[c[i]]) c[i+1]=l2[c[i]],vis[c[i+1]]=1;
else if(c[i-1]==l2[c[i]]) c[i+1]=l1[c[i]],vis[c[i+1]]=1;
else
{
si=0;
printf("-1\n"); return ;
}
}
for(int i=1;i<=n;i++) if(!vis[i]) si=0,printf("-1\n");
//前面没判断c[n]同学的需求是否满足,这里判断一下。
if((c[1]==l1[c[n]]&&c[n-1]!=l2[c[n]])||(c[1]!=l1[c[n]]&&c[n-1]==l2[c[n]])) si=0,printf("-1\n");
else if((c[1]==l2[c[n]]&&c[n-1]!=l1[c[n]])||(c[1]!=l2[c[n]]&&c[n-1]==l1[c[n]])) si=0,printf("-1\n");
}
void Simulation()//求答案
{
for(int i=1;i<=n;i++)
{
Dv1[(c[i]-i+n)%n]++;
Dv2[(c[n-i+1]-i+n)%n]++;
}
for(int i=0;i<=n-1;i++) ans=max(ans,max(Dv1[i],Dv2[i]));
printf("%d\n",n-ans);
}
void read_ini()
{
n=read();
for(int i=1;i<=n;i++) l1[i]=read(),l2[i]=read();
Build();
if(si) Simulation();
}
int main()
{
read_ini();
return 0;
}
最后推荐一个ppt,包括noip2005所有题目的题解 Rp++ https://wenku.baidu.com/view/878beb64783e0912a2162aa7.html?qq-pf-to=pcqq.c2c