题目链接:
题意:
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");
}
}