【置换群/模拟】NOIP2005-篝火晚会

【问题描述】

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有n个同学,编号从1到n。一开始,同学们按照1,2,……,n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。

佳佳可向同学们下达命令,每一个命令的形式如下:

(b1, b2,... bm -1, bm)

这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1,b2,…… bm -1,bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。

执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

【输入文件】

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

【输出文件】

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

【样例输入】

4

3 4

4 3

1 2

1 2

【样例输出】

2

【数据规模】

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

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

【思路】

假设起始状态为1...n。首先以第一个孩子为起点,模拟实现一个环的目标状态。此时如果建环过程中出现矛盾,直接输出-1;否则,一定可以通过若干次操作移动到目标状态。

由于是环,起点位置可以改变,而且可以正序倒序两种方式。有以下几个结论:

(1)两个数,如果它们和起始状态的差加上n再mod n的结果一样,说明它们共同移动某步,能移动到目标状态。看下面这个例子:

起始状态:1 2 3 4 5

目标状态:1 5 3 2 4

差值:     0 3 0 -2 -1

我们可以发现,对于5和2,(3%5=3),((-2+5)%5=3)相等。而这两个数向右移动两位,都可以到达目标状态。

(2)每次移动一个价值,一定能使得一个数到达自己的目标状态。也就是说,有几个数与目标状态不对应,就需要消耗多少价值。

由此我们可以明白:

把差值(加n %n后)相同个数的最大值max求出来,然后把这几个数假设就在目标状态,那么移动需要的价值为n-max。因为环可以正反,正反各来一次即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int MAXN= 50000+500;
 8 int n;
 9 int want[MAXN][2];
10 int q[MAXN];
11 
12 void init()
13 {
14     scanf("%d",&n);
15     for (int i=0;i<n;i++)
16     {
17         scanf("%d%d",&want[i][0],&want[i][1]);
18         want[i][0]--;
19         want[i][1]--;
20     }
21 }
22 
23 int canmake()
24 {
25     q[0]=0;
26     q[1]=want[0][0];
27     for (int i=1;i<n-1;i++)
28     {
29         if (q[i-1]!=want[q[i]][0]) q[i+1]=want[q[i]][0];
30             else if (q[i-1]!=want[q[i]][1]) q[i+1]=want[q[i]][1];
31                 else return 0;
32     }
33     if (want[q[n-1]][0]!=0 && want[q[n-1]][1]!=0) return 0;
34     for (int i=0;i<n;i++) cout<<q[i]<<endl;
35     return 1;
36 }
37 
38 void submain()
39 {
40     int ans=0;
41     int appear[MAXN];
42     int appear2[MAXN];
43     memset(appear,0,sizeof(appear));
44     memset(appear2,0,sizeof(appear2));
45     for (int i=0;i<n;i++)
46     {
47         appear[(q[i]-i+n)%n]++;
48         appear2[(q[i]+i-1)%n]++;
49         if (appear[(q[i]-i+n)%n]>ans) ans=appear[(q[i]-i+n)%n];
50         if (appear2[(q[i]+i-1)%n]>ans) ans=appear2[(q[i]+i-1)%n];
51     }
52     cout<<n-ans<<endl;
53 }
54 
55 int main()
56 {
57     //freopen("fire9.in","r",stdin);
58     //freopen("fire9.out","w",stdout);
59     init();
60     if (canmake()) submain();
61         else cout<<-1<<endl;
62     system("pause");
63     return 0;
64 }

 

转载于:https://www.cnblogs.com/iiyiyi/p/4856697.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值