P1053 [NOIP2005 提高组] 篝火晚会

题目描述

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有 �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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值