[codeforces 1343F] Restore the Permutation by Sorted Segments 从假定出发的尝试

本文详细解析了Codeforces Round#636 (Div.3)中的F题“Restore the Permutation by Sorted Segments”。通过从前向后尝试构建原排列的方法,阐述了解题思路和步骤,并提供了AC代码示例。

Codeforces Round #636 (Div. 3)   比赛人数12253

[codeforces 1343F]   Restore the Permutation by Sorted Segments   从假定出发的尝试

总目录详见https://blog.csdn.net/mrcrack/article/details/103564004

在线测评地址http://codeforces.com/contest/1343/problem/F

ProblemLangVerdictTimeMemory
F - Restore the Permutation by Sorted Segments GNU C++17Accepted46 ms200 KB

思路摘自https://www.cnblogs.com/heyuhhh/p/12753964.html

考虑从前往后构造出原排列。
假设我们知道了第一个位置的元素,那么据此可以求出第二个位置的元素,以此类推就可以求出所有的元素。
已知第i-1个元素,求第i个元素的方法:我们考虑所有的序列,如果一个序列中删光已被确定位置的数后,未出现的数的个数为1,我们此时就可以选定该未出现的数其作为第i个数。
这样做的正确性是显然的,如果我们不选这个元素而选其它的元素,那么显然这个元素和序列中其它元素不在一个序列中。
那么我们直接枚举第一个元素,然后依次找后面的元素即可。如果找到合法的就直接输出。

PS.其实这个题最容易的想法是从后往前找,因为每次最后一个位置的数只会出现一次,但最前面的数也可能出现一次。这样的话就需要多考虑一些情况来判断当前是否合法,码量会很大。但是如果注意到了这样一个性质就比较容易想到从第一个开始找,就是每次我们开头都是一个长度为2的区间,删去第一个后很容易找到下一个,下一个也是一个长度为2的区间的开头...然后以此类推。从后往前的话就不具备这样的性质。

若不明白上述说法,请看样例模拟如下

5
2 2 5
3 2 3 5
4 2 3 4 5
5 1 2 3 4 5

2 5 3 4 1

1.假定位置1值是1
位置    1 2 3 4 5
数值    1 0 0 0 0

寻找位置2的数值
数据段2 5,没有已固定位置的数据可删除,故在此次判定中无用
数据段2 3 5,没有已固定位置的数据可删除,故在此次判定中无用
数据段2 3 4 5,没有已固定位置的数据可删除,故在此次判定中无用
数据段1 2 3 4 5,删除已固定位置的数值1,成为了数据段2 3 4 5,
但还是无法判定剩下的数据位置,故在此次判定中无用

经尝试,位置1值是1,假定错误。

2.假定位置1值是2
位置    1 2 3 4 5
数值    2 0 0 0 0

寻找位置2的数值
数据段2 5,删除已固定位置的数值2,成为了数据段5,故可让位置2值是5
位置    1 2 3 4 5
数值    2 5 0 0 0


寻找位置3的数值
数据段2 3 5,删除已固定位置的数值2,5,成为了数据段3,故可让位置3值是3
位置    1 2 3 4 5
数值    2 5 3 0 0


寻找位置4的数值
数据段2 3 4 5,删除已固定位置的数值2,5,3,成为了数据段4,故可让位置4值是4
位置    1 2 3 4 5
数值    2 5 3 4 0


寻找位置5的数值
数据段1 2 3 4 5,删除已固定位置的数值2,5,3,4,成为了数据段1,故可让位置5值是1
位置    1 2 3 4 5
数值    2 5 3 4 1

经尝试,位置1值是2,假定正确。

AC代码如下

最难懂的代码

else if(pos[b]>=i-a[j][0]+1)r--;//我们认为a[j][]右边界对应位置i,a[j][]左边界起始位置对应i-a[j][0]+1,pos[b]必须落在[i-a[j][0]+1,i]范围之内,才能被删除,故pos[it]>=i-a[j][0]+1

#include <stdio.h>
#define maxn 205
int n,a[maxn][maxn],pos[maxn],ans[maxn];
int judge(){
	int i,j,k,r,x,b;
	for(i=2;i<=n;i++){//i代表位置,从位置2开始寻找
		for(j=1;j<n;j++){//尝试给定的数据段,请注意,我们让尝试的数据段右边界与i重合
			r=a[j][0],x=-1;
			for(k=1;k<=a[j][0];k++){
				b=a[j][k];
				if(!pos[b])x=b;
				else if(pos[b]>=i-a[j][0]+1)r--;//我们认为a[j][]右边界对应位置i,a[j][]左边界起始位置对应i-a[j][0]+1,pos[b]必须落在[i-a[j][0]+1,i]范围之内,才能被删除,故pos[it]>=i-a[j][0]+1
			}
			if(r==1&&x!=-1){ans[i]=x,pos[x]=i;break;}//找到i位置的数据
		}
		if(ans[i]==0)return 0;//若找不到
	}
	return 1;
}
int main(){
	int t,i,k,j;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(i=1;i<n;i++){
			scanf("%d",&k),a[i][0]=k;//a[i][0]=k存储该段数据长度
			for(j=1;j<=k;j++) scanf("%d",&a[i][j]);
		}
		for(i=1;i<=n;i++){//尝试第一个位置的值是i
			for(j=1;j<=n;j++)ans[j]=0,pos[j]=0;//初始化
			ans[1]=i;//第一个值是i
			pos[i]=1;//i值所处的位置是1
			if(judge())break;
		}
		for(i=1;i<n;i++)printf("%d ",ans[i]);
		printf("%d\n",ans[n]);	
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值