習題: Vladik and cards(狀壓DP)
阿新 • • 發佈:2020-08-09
題目
思路
比較顯然的一點,這道題對於一個方案,所有出現的數字的最小出現次數是有單調性的
考慮二分出所有出現數字的最小出現次數
設\(dp[i][j]\)表示前i個數,已經達到目標狀態的數的狀態為j
因為題目中要求數是連續的
所以我們只需要考慮接下來的一段全部是哪一個數字即可,用這一點進行轉移
最後考慮是否有一個\(dp[i][(1<<8)-1]\)合法即可
程式碼
#include<iostream> #include<cstring> #include<vector> using namespace std; int n; int a[1005]; int l,r,mid,ans; int t[1005]; int dp[1005][(1<<8)]; vector<int> pos[1005]; int check(int cnt) { memset(t,0,sizeof(t)); memset(dp,-0x3f,sizeof(dp)); int bas=dp[0][0]; dp[1][0]=0; for(int i=1;i<=n;i++) { for(int j=0;j<(1<<8);j++) { if(bas!=dp[i][j]) { for(int k=0;k<8;k++) { if(!(j&(1<<k))) { int nxt=cnt+t[k]-1; if(nxt>=pos[k].size()) continue; dp[pos[k][nxt]][j|(1<<k)]=max(dp[pos[k][nxt]][j|(1<<k)],dp[i][j]); nxt++; if(nxt>=pos[k].size()) continue; dp[pos[k][nxt]][j|(1<<k)]=max(dp[pos[k][nxt]][j|(1<<k)],dp[i][j]+1); } } } } t[a[i]-1]++; } int ret=-1; for(int i=1;i<=n+1;i++) ret=max(ret,dp[i][(1<<8)-1]); return ret==-1?-1:ret+cnt*8; } int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; pos[a[i]-1].push_back(i); } l=0; r=n*8; while(l+1<r) { mid=(l+r)>>1; int t=check(mid); if(t!=-1) { ans=max(ans,t); l=mid; } else r=mid; } //cout<<"end"; while(check(l+1)!=-1) { //cout<<l+1<<' '<<check(l+1)<<endl; ans=max(ans,check(l+1)); l++; } if(ans==0) { for(int i=0;i<8;i++) if(pos[i].size()) ans++; } cout<<ans; return 0; }