P1053 篝火晚会
Forever丶CIL · · 题解
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