2021牛客OI賽前集訓營-提高組(第五場)C-第K排列【dp】
阿新 • • 發佈:2021-10-14
正題
題目連結: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\)
然後根據\(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; }