洛谷 P1053篝火晚会题解--zhengjun

题目描述

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

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

\((b_1, b_2,... b_{m-1}, b_m)\)这里\(m\)的值是由佳佳决定的,每次命令\(m\)的值都可以不同。这个命令的作用是移动编号是\(b_1,b_2,…, b_m\)的这\(m\)个同学的位置。要求\(b_1\)换到\(b_2\)的位置上,\(b_2\)换到\(b_3\)的位置上,……,要求\(b_m\)换到\(b_1\)的位置上。执行每个命令都需要一些代价。我们假定如果一个命令要移动\(m\)个人的位置,那么这个命令的代价就是\(m\)。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

输入格式

第一行是一个整数\(n(3 \le n \le 50000)\),表示一共有\(n\)个同学。

其后\(n\)行每行包括\(2\)个不同的正整数,以一个空格隔开,分别表示编号是\(1\)的同学最希望相邻的两个同学的编号,编号是\(2\)的同学最希望相邻的两个同学的编号,……,编号是\(n\)的同学最希望相邻的两个同学的编号。

输出格式

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

输入输出样例

输入 #1 复制
4
3 4
4 3
1 2
1 2
输出 #1 复制
2

说明/提示

对于\(30\%\)的数据,\(n \le 1000\)
对于全部的数据,\(n \le 50000\)

\(2005\)提高组第三题

思路

首先,为了让总代价最少,一定就要让移动的人次最少,我们方法就是找到一组尽可能多的不用移动的,而剩下的一坨,有可能是一次换的,有可能是两次……但是,无论是多少次,最少的代价一定就是总个数,因为每个人只需要换一次就可以到达他的位置,连成一个环,如果还有其他的环,就再每人换一次,又成一个环,知道换完位置。这样每个人就只需要移动一次就够了。

那么,问题就转换成了求最多可以有多少个人不用动。

所以,我就打了一个模拟,直接从头到尾看看有多少不用动,结果,听取\(wa\)声一片

额,我再乍一看,一个是初始环,另一个是目标环,要找到最多能有多少一样的,应该要一次一次对过去,然后,有一个方法,就是求出他要移动多少格才可以到达最后的目标点。

这样,移动格数相同的就一定是同一组的,也就是同一次移动的,然后找到最多的一组就可以了。

结果代码一交,又是\(wa\)

然后又细细一想,啊,目标环还有可能是另一个方向摆的,就是比如说样例,可能是1 3 2 4,也有可能是1 4 2 3,所以就要两次。

结果交上去,终于\(AC\)了(太不容易了

代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define maxn 50001
using namespace std;
int n;
int a[maxn],b[maxn];//每个点的两边的点
bool flag[maxn];//判断是否已经用过了
int k[maxn],f[maxn];//目标序列,距离目标点有多少距离
int sum[maxn];//桶计数
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
	int now=1;
	k[1]=1;
	flag[now]=1;
	for(int i=2;i<=n;i++){
		if(!flag[a[now]]){//左边的点还没有用过
			k[i]=a[now];
			now=a[now];
			flag[now]=1;
		}
		else if(!flag[b[now]]){//右边的点还没用过
			k[i]=b[now];
			now=b[now];
			flag[now]=1;
		}
		else{//两边的点都用过了,判无解
			printf("-1");
			return 0;
		}
	}
	for(int i=1;i<=n;i++)f[i]=(i-k[i]+n)%n;//求距离
	int maxx=0;
	for(int i=1;i<=n;i++){
		sum[f[i]]++;//桶计数
		maxx=max(maxx,sum[f[i]]);//求最大值
	}
	//重来一遍
	memset(sum,0,sizeof(sum));//清空
	memset(flag,0,sizeof(flag));//清空
	now=1;
	k[1]=1;
	flag[now]=1;
	for(int i=2;i<=n;i++){
		if(!flag[b[now]]){//反过来就可以求另一种顺序了
			k[i]=b[now];
			now=b[now];
			flag[now]=1;
		}
		else if(!flag[a[now]]){
			k[i]=a[now];
			now=a[now];
			flag[now]=1;
		}
	}
	for(int i=1;i<=n;i++)f[i]=(i-k[i]+n)%n;
	for(int i=1;i<=n;i++){
		sum[f[i]]++;
		maxx=max(maxx,sum[f[i]]);
	}
	printf("%d",n-maxx);//注意最后要用来减
	return 0;
}

谢谢--zhengjun

posted @ 2022-06-10 19:18  A_zjzj  阅读(48)  评论(0编辑  收藏  举报