NOIP2005篝火晚会

NOIP2005篝火晚会题解

题目描述 Description

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了小教官。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有n个同学,编号从1n。一开始,同学们按照12……n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。
佳佳可向同学们下达命令,每一个命令的形式如下:
(b1,b2,...bm-1,bm)
这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1b2…… bm –1bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。
执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

输入描述 Input Description

输入第一行是一个整数n3<=n<=50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。

输出描述 Output Description

这一行只包含一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出-1。

样例输入 Sample Input

4

4

4 3

1 2

1 2

样例输出 Sample Output

2

数据范围及提示 Data Size & Hint


【数据规模】



对于30%的数据,n<=1000

对于全部的数据,n<=50000

    这道题在CodeVS上的难度是“大师Master”,说实在的,我真没有觉得它有那么难。下面步入正题。

    30%

    第一个部分数据是解决整道题的开始,很多分析部分数据时得到的结论会一直贯穿下去,一直到设计出完美的算法,所以每一个部分数据都是很重要的。

    题目中所提到的命令可以概括的说为,任意选择(即可以无序,且不必相邻)m个人,让他们交换位置,且交换位置的过程是一个环,比如a->b->c->d->a,这样的代价为4,明显地 ,当一个人到达了他想在的位置后,就永远不需要再挪动了,而且每个人到达它想在的位置的代价必定为1,因此,当目标序列确定时,不在位人数即为这种情况下的解。所以就需要先构造一个初始目标环,每次让所有元素向右旋转(对应到数组就是所有元素右移),N次下来,统计每次的代价,取最小值即为答案。需要关注的是,由于题目没指明环的方向,所以逆向也要做一遍。时间复杂度O(N^2)

    对于无解的判断:明显地,如果目标环存在,则一定有解。否则无解,我的方法是进行一次遍历,如果能够正好经过N个点,则有解,否则无解。

    100%

    以下要说的方法是我在一篇博文中看到的。30%的方法中,我把每种情况都枚举,这是没必要的。在目标环中,如果一些元素的相对位置与原环中的相同,我就可以让两个环中的这些元素对齐,此时若这些元素的个数为k,则代价m=N-k。明显地,找出最长的一组就可以让代价最小。

    要找最长的相对位置相同的串,也就是找最长的递增且权值之差等于序号之差的序列,DP可行,加入优化可以到O(nlogn)。还有一种简单的方法,如果a和b的相对位置与序号的相对位置和c和d的相对位置与序号的相对位置相同,并无法确定a、b、c、d相对位置是否都与序号的相对位置相同。但是我如果知道a与其序号位置、b与其序号位置、c与其序号位置、d与其序号位置的差值都相等,就一定有a、b、c、d与序号的相对位置相等。因此用a[i]存储目标环中权值与序号差值为i的数的个数,最后找出最大值k,N-k即为解。同样要反向做一次。时空复杂度皆为O(N)。

代码:

<span style="font-family:Arial;">//NOIP2005提高组 篝火晚会 神奇算法 
#include <cstdio>
#include <algorithm>
#define maxn 50000
#define inf 0x3f3f3f3f

using namespace std;

int cir[maxn], N, want[maxn][3], start, a[maxn], b[maxn];

void input()
{
	int i, a, b;
	scanf("%d",&N);
	for(i=0;i<N;i++)
	{
		scanf("%d%d",&a,&b);
		a--, b--;
		want[i][1]=a, want[i][2]=b;
	}
}

bool check()
{
	int i, cnt, a, b;
	bool flag[maxn]={0};
	i=0;
	flag[0]=true;
	cnt=1;
	while(true)
	{
		a=want[i][1];
		b=want[i][2];
		if(!flag[a])
		{
			flag[a]=true;
			cnt++;
			i=a;
		}
		else if(!flag[b])
		{
			flag[b]=true;
			cnt++;
			i=b;
		}
		else break;
	}
	if(cnt==N)return true;
	else return false;
}

int work()
{
	int ans=inf, i, t;
	if(!check())return -1;
	cir[0]=0;
	cir[1]=want[0][1];
	for(i=1;i<N-1;i++)
		if(cir[i-1]==want[cir[i]][1])
			cir[i+1]=want[cir[i]][2];
		else cir[i+1]=want[cir[i]][1];
	for(i=0;i<N;i++)
	{
		a[(i-cir[i]+N)%N]++;
		b[(N-1-i-cir[i]+N)%N]++;
	}
	t=-inf;
	for(i=0;i<N;i++)
		t=max(t, max(a[i],b[i]) );
	return N-t;
}

int main()
{
	input();
	printf("%d\n",work());
	return 0;
}</span>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值