习题: Vladik and cards(状压DP)
题目
思路
比较显然的一点,这道题对于一个方案,所有出现的数字的最小出现次数是有单调性的
考虑二分出所有出现数字的最小出现次数
设\(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;
}

浙公网安备 33010602011771号