C. Shinju and the Lost Permutation

题目链接:

Problem - 1658C - Codeforces

题意:

t个样例,每个样例给定一个数字n和一个长度为n的序列,序列里的每一个值对应一个power,问是否存在一个从1-n的全排列能得到给定的这个power序列,存在输出"Yes",否则输出"No"。

对从1-n的一个全排列,比如n=6时的一个全排列3 2 5 4 6 1,其power值为3,全排列中小的值会被前面大的覆盖,即3 3 5 5 6 6,此时序列中有3个不同的值,power就等于。这是第一个power值,接下来将最后一位移到前面,得到1 3 2 5 4 6,对应power值为4(1,3,5,6),这是第二个power值,接下来再将最后一位移到前面,得到6 1 3 2 5 4,此时power=1,如此进行下去,直到全排列最初的第一位变成最后一位,便得到整个power序列为3 4 1 2 2 3。

样例

输入

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

输出

YES
YES
NO
NO
YES
NO

思路:

碰见这种序列找规律的题,可以先打个表(打表的代码我后面也附上了)。

一开始自己在草稿纸上拿n=3和n=6的几个全排列尝试手推了一下,发现了一些规律。

比如给的序列中有且只能有一个1。

除了1以外,当前值与前一个相等或大于1,不满足就不成立(这个不全面,wa也是wa在这上面了,打表后也找到了反例,除1外,当前值与前一个值之差只要是不大于1都是可以的)。所以以后能打表还是打打表,打完表后对照结果验证自己猜测的结论。

接下来,说说为什么除唯一的1个1外,当前值与前一个值之差只要是不大于1都是可以的。对于给定的power序列,1前面的可以接到后面,是等价的(接完后成立,接之前也成立,只不过接了几位就倒着移几次),如2 3 1 2 3 4就等价于1 2 3 4 2 3。我们以n=7为例,一开始全排列s开头是     7 *,对应power为1,接下来最后面一位无论是几,都power都变为2。

假如power值是依次是1 2 3 4,那s调整成1 3 6 7 *,当前power值是4,那么接下来的power值可以是2 3 4 5;

power为5,那就是拿比第一个数还小的数,放到前面,这里可以是0.5 1 3 6 7 *,  重新调整s成    1 2 3 4 7  *。

power为4,那就是拿一个大于第1个数小于第2个数的数放到最前面,可以是2 1 3 6 7 *。

power为3,拿一个大于第2个数小于第3个数的数放到最前面,可以是4.5 1 3 6 7 *,调整成4 1 3 6 7 *。

power为2,拿一个大于第3个数小于第4个数的数放到最前面,可以是6.5 1 3 6 7 *,重新调整s成    4 1 2 3 7 *。

把后面一个数拿到前面最多就多1,大于1了肯定就是错的。

代码: 

打表代码:

#include <bits/stdc++.h>

using namespace std;

int diff(int a[],int len){
	bool vis[200];
	int cnt=0;
	memset(vis,false,sizeof(vis));
	for(int i=1;i<=len;i++){
		vis[a[i]]=true;
	}
	for(int i=1;i<=len;i++){
		if(vis[i])
			cnt++;
	}
	return cnt;
}
void printTable(int n){
	int a[200],b[200],c[200];
	for(int i=1;i<=n;i++){
		a[i]=i;
	}
	int res;
	do{
		for(int i=1;i<=n;i++){
			b[i]=a[i];
		}
		for(int i=1;i<=n;i++){
			c[1]=b[1];
			for(int j=2;j<=n;j++){
				c[j]=max(c[j-1],b[j]);
			}
			res=diff(c,n);
			cout<<res<<" ";
			int tmp=b[n];
			for(int j=n;j>=2;j--){
				b[j]=b[j-1];
			}
			b[1]=tmp;
		}
		cout<<endl;
	}while(next_permutation(a+1,a+n+1));
} 

int main(){
	int n;
	scanf("%d",&n);
	printTable(n);
}

AC代码:

#include <bits/stdc++.h>

using namespace std;

const int N=1e5+10;
int t,n;
int a[N];

int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		int one=0,pos;//1的数量和位置 
		for(int i=0;i<n;i++){
			scanf("%d",&a[i]);
			if(a[i]==1){
				one++;
				pos=i;
			}
		}
		if(one!=1){//1的数量不是1个,肯定是错的 
			printf("No\n");
			continue;
		}
		int pre=1; 
		bool flag=true;
		for(int i=1;i<n;i++){
			int rp=(i+pos)%n;//通过取余的方式将1之前的接到后面
			if(a[rp]-pre>1){
				flag=false;
				break;
			}
			pre=a[rp];
		}
		if(flag)
			printf("Yes\n");
		else
			printf("No\n");
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值