这种题 看起来题意都会很复杂的:
对于 1-n的序列 :比如4.1.6.2.5.3 。。。。 1对应4,4对应2,所以(142)是一个集合,同理有(36)(5)
现在要将这些集合排序,集合内部按从大到小排列,集合与集合之间按第一个数从小到大排。比如:
上述的序列 变成:(421)(5)(63)
现在定义一些序列,经过上述的操作之后不会发生改变,比如:1 3 2 4 ; (1)(32)(4)
现在给出n,k。
求1-n的序列中 所有不会操作之后不会改变的序列, 并且按字典序排的第k个
比如样例:
n=4 有
1 2 3 4
1 2 4 3
1 3 2 4
2 1 3 4
2 1 4 3
第三个就是1 3 2 4
我刚刚开始看到这道题,想到的就是 前几天做的经典dp,栅栏高低起伏的那道题, 这道题应该也是一个一个数字的确定过去
但是很难想明白 以1 开头的序列到底有多少个
但其实可以写一写,枚举一下n的序列:
>
n=1———n=2———–n=3———–n=4—– - - - - - -n=5
1————1 2 ———–1 2 3———-1 2 3 4———–1 2 3 4 5
———— -2 1 ———–2 1 3——- -2 1 3 4———–2 1 3 4 5
…—————————1 3 2———1 3 2 4———–1 3 2 4 5
.——————————————–1 2 4 3———–1 2 4 3 5
.——————————————–2 1 4 3———–2 1 4 3 5
.————————————————————–1 2 3 5 4
.————————————————————–2 1 3 5 4
.————————————————————–1 3 2 5 4
仔细看看其实不难得出规律吧:
每一位,如果想要造作之后不变,那么他永远都只能和他前一位的数字是出于同一个集合,否则都不能满足不改变这个条件!
那么就分成了两个子问题:
1.如果i和i-1 进行交换,那么dp[i]+=dp[i-2]
2.i 不和 i-1 交换 dp[i]+=dp[i-1]
也许你无法证明,但是仔细看看写的这些东西,感觉还挺有道理的。于是就可以得到每一个i ,yes(和前一位交换)的个数,以及no(不和前一位交换)的个数,并能用递归预处理求出1-50 的所有dp
struct node{
__int64 yes;
__int64 no;
__int64 sum;
}dp[60];
dp[1].yes=dp[1].no=dp[1].sum=1;
dp[2].yes=dp[2].no=1;dp[2].sum=2;
dp[3].yes=1;
for(int i=3;i<=50;i++){
dp[i].no=dp[i-1].no+dp[i-2].no;
}
for(int i=4;i<=50;i++){
dp[i].yes=dp[i-1].yes+dp[i-2].yes;
dp[i].sum=dp[i].yes+dp[i].no;
}```
想到这一步我们就可以开始一步一步的逐个确认了,
仔细看看n=5,是 2和1 交换的个数 =5和4交换的个数 =dp[5].yes
cnt初识为2,
若是k<no,那么 2和1 不会交换,则ans[cnt-1]=cnt-1,让pos--,cnt++ 继续判断下一个
否则 ,2和1 交换,则ans[cnt-1]=cnt,ans[cnt]=cnt-1 ; 因为交换,所以可以确定两个位置pos-2,cnt+=2
k-=dp[pos].no;
然后我就开始xjb写了,写了大半天还好是写出来了:
ac代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#define mem(a) memset(a,0,sizeof(a))
#define ll __int64
#define INF 0x7fffffff //INT_MAX
#define inf 0x3f3f3f3f //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
#define mod 1000000007
const int maxn=2000001;
struct node{
__int64 yes;
__int64 no;
__int64 sum;
}dp[60];
int ans[55];
int main(){
freopen("1.txt","r",stdin);
int n;
__int64 k;
dp[1].yes=dp[1].no=dp[1].sum=1;
dp[2].yes=dp[2].no=1;dp[2].sum=2;
dp[3].yes=1;
for(int i=3;i<=50;i++){
dp[i].no=dp[i-1].no+dp[i-2].no;
}
for(int i=4;i<=50;i++){
dp[i].yes=dp[i-1].yes+dp[i-2].yes;
dp[i].sum=dp[i].yes+dp[i].no;
}
// printf("%I64d %I64d \n",dp[46].yes,dp[47].yes);
while(~scanf("%d %I64d",&n,&k)){
int pos=n;
int cnt=2;
mem(ans);
while(cnt<=n ){
// printf("pos=%d\n",pos);
if(dp[pos].no>=k){ //从低位找 不换,再找交换的
pos--;
ans[cnt-1]=cnt-1;
cnt++;
}
else if(dp[pos].no<k){
// printf("yes %d\n",cnt);
k-=dp[pos].no;
pos-=2;
ans[cnt-1]=cnt;
ans[cnt]=cnt-1;
cnt+=2;
}
}
for(int i=1;i<=n;i++){
if(!ans[i]) printf("%d ",i);
else printf("%d ",ans[i]);
}
cout<<endl;
}
return 0;
}
“`