正题
题目链接:https://ac.nowcoder.com/acm/contest/20110/C
题目大意
一个长度为\(n\)的字符串\(S\),\(S\)中存在一些\(?\),有\(N/O/I/P\)四个字符作为字符集,每对相邻的字符会产生不同的贡献,现在要求所有权值不小于\(x\)的字符串中字典序第\(k\)大的。
\(1\leq n,k\leq 1000,1\leq x\leq 10^9\)
解题思路
考虑到暴力\(dfs\)搜索的瓶颈在于我们可能会搜到大量权值小于\(x\)的序列,所以如果保证我们每次都能搜到满足条件的字符我们就可以\(O(nk)\)解决这个问题。
可以设\(f_{i,j}\)表示\(i\)个字符是\(j\)开始往后最多能得到多少权值。
然后根据\(dp\)数组暴力\(dfs\)就好了。
时间复杂度:\(O(nk)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1100;
ll n,l,k,w[4][4],f[N][4],a[N],c[N];
char s[N];
void dfs(ll x,ll p,ll sum){
if((c[x]!=-1&&c[x]!=p)||sum+f[x][p]<l||!k)return;
a[x]=p;
if(x==n&&sum>=l){
k--;
if(!k){
for(ll i=1;i<=n;i++){
if(a[i]==0)putchar('N');
if(a[i]==1)putchar('O');
if(a[i]==2)putchar('I');
if(a[i]==3)putchar('P');
}
putchar('\n');
}
return;
}
dfs(x+1,3,sum+w[p][3]);
dfs(x+1,1,sum+w[p][1]);
dfs(x+1,0,sum+w[p][0]);
dfs(x+1,2,sum+w[p][2]);
return;
}
signed main()
{
scanf("%lld%lld%lld",&n,&l,&k);
scanf("%s",s+1);
for(ll i=1;i<=n;i++){
if(s[i]=='N')c[i]=0;
if(s[i]=='O')c[i]=1;
if(s[i]=='I')c[i]=2;
if(s[i]=='P')c[i]=3;
if(s[i]=='?')c[i]=-1;
}
for(ll i=0;i<4;i++)
for(ll j=0;j<4;j++)
scanf("%lld",&w[i][j]);
memset(f,0xcf,sizeof(f));
for(ll i=0;i<4;i++)
if(c[n]==-1||c[n]==i)f[n][i]=0;
for(ll i=n-1;i>=1;i--)
for(ll j=0;j<4;j++){
if(!(c[i]==-1||c[i]==j))continue;
for(ll k=0;k<4;k++)
f[i][j]=max(f[i][j],f[i+1][k]+w[j][k]);
}
dfs(1,3,0);dfs(1,1,0);dfs(1,0,0);dfs(1,2,0);
if(k)puts("-1");
return 0;
}