这题竟然是数位dp,我看了别人的代码才会做的。
状态时dp[l][r][rev][inv]
表示当前考虑l,r这段区间
rev为1表示现在数字已经比到过来的大了,反之为0
inv为1表示现在数字已经比倒过来取反的大了,反之为0
dp表示这样的数字有多少种。
我们也就是说我们考虑的数字就是每组数字中最小的那个,我们保证了他会比倒过来和倒过来取反要小。转移的时候有很多条件,就是为了保证这个。
这样枚举每一位填什么就可以了。
#include <bits/stdc++.h>
using namespace std;
int n,a[100];
bool vis[55][55][2][2];
long long dp[55][55][2][2];
long long m;
long long solve(int l,int r,int rev,int inv)
{
if(l>r)
return 1;
if(vis[l][r][rev][inv])
return dp[l][r][rev][inv];
vis[l][r][rev][inv]=1;
long long &ans=dp[l][r][rev][inv];
ans=0;
for(int i=0;i<2;i++)
{
if(a[l]==-1||a[l]==i)
{
for(int j=0;j<2;j++)
{
if(a[r]==-1||a[r]==j)
{
if(l<r||i==j)
{
if(rev||i<=j)
{
if(inv||i<=1-j)
{
ans=ans+solve(l+1,r-1,i<j||rev,i<1-j||inv);
}
}
}
}
}
}
}
return ans;
}
int main()
{
cin>>n>>m;
m++;
memset(a,-1,sizeof(a));
a[0]=0;
if(solve(0,n-1,0,0)<m)
{
puts("-1");
return 0;
}
for(int i=1;i<n;i++)
{
memset(vis,0,sizeof(vis));
a[i]=0;
long long cur=solve(0,n-1,0,0);
if(cur<m)
{
m-=cur;
a[i]=1;
}
}
for(int i=0;i<n;i++)
{
printf("%d",a[i]);
}
//system("pause");
return 0;
}