nyoj 小明组织活动的任务 59

142 篇文章 0 订阅

小明组织活动的任务

时间限制: 2000 ms | 内存限制: 65535 KB
难度: 6
描述
小明刚进高中,在军训的时候,由于小明吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,小明被命令组织同学们进行篝火晚会。一共有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。我们需要小明用最少的总代价实现同学们的意愿,聪明的小明也有犯傻的时候,你能帮助小明吗?
n <= 50000。
输入
第一行输入N(0<N<10)表示测试数据组数,每组测试数据输入的第一行是一个整数n(3 <= n <= 50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。
输出
每组测试数据包括输出包括一行,这一行只包含一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出-1。
样例输入
1
4
3 4
4 3
1 2
1 2
样例输出
2
//感觉好难,看了大神的代码都没看懂
//下面是大神的思路及代码

大概思路:首先获得数据后判断数据是否满足各个人的需求,按简单的模拟就好一遍循环,线形时间。同时获得了目标序列的某种情况,因为是环,所以看成链的话总共有2n种情况。开始写了个O(n^2)的,没有计算数据规模,果断超时了,然后知道用置换群,啃书2天,十分难懂,这里先不说了,核心思想说一下:要求出目标环与初始环间最多有多少个元素的位置可能相同,只要求出目标环各元素值与初始环对应位置的元素值之差的结果相同的最多是多少个。十分好理解,每次做差的结果就是该元素与其应该所处的目标位置的偏移量。

for (int i=1;i<=n;i++)

{

temp=(n+en[i]-i)%n;

searchSame[temp]++;

}

这是从左向右判断,还有就是出现镜像的一种合法链,所以需要再从反方向做一边该操作。

最终获得最大的相同位置的个数。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 50010
int a[N];//a[]最终目标序列,res是en合法标记
int b[N][3];//各个需求 [][0]标记该人是否入列,[][1],[][2]为左右需求
int c[N];//置换群求相同位置时使用
int res;
//判断数据是否能够满足要求 
void judge(int n)
{
	a[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(b[b[a[i-1]][1]][0]==0)
		{
			a[i]=b[a[i-1]][1];
			b[a[i-1]][0]=1;
		}
		else if(b[b[a[i-1]][2]][0]==0)
		{
			a[i]=b[a[i-1]][2];
			b[a[i-1]][0]=1;
		}
		else
		{
			res=-1;
			return ;
		}
		
	}
	if((b[a[n]][1]==a[1]||b[a[n]][1]==a[n-1])&&(b[a[n]][2]==a[1]||b[a[n]][2]==a[n-1]))
		return ;
	else
		res=-1;
}
//计算需求数列与原数列对比,无需移位的个数的最大值mm
int f(int n)
{
	int mm=0,tmp;
	memset(c,0,sizeof(c));
	tmp=0;
	for(int i=1;i<=n;i++)
	{
		tmp=(n+a[i]-i)%n;
		c[tmp]++;
	}
	for(int i=0;i<=n;i++)
		if(mm<c[i])
			mm=c[i];
	//以下部分为上述代码的镜像操作
	memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++)
	{
		tmp=(n+a[n-i+1]-i)%n;
		c[tmp]++;
	}
	for(int i=0;i<=n;i++)
		if(mm<c[i])
			mm=c[i];
	return n-mm;
}
int main()
{
	int t,n;
	scanf("%d",&t);
	while(t--)
	{
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		res=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d%d",&b[i][1],&b[i][2]);
		judge(n);
		if(res!=-1)
			printf("%d\n",f(n));
		else
			printf("-1\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值