NOIP2005篝火晚会

来源:互联网 发布:node 部署web应用程序 编辑:程序博客网 时间:2024/04/27 22:34

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 0x3f3f3f3fusing 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>


0 0