标签:and 状压dp add problem 注意 tar 图计算 特殊 辅助
? 把子序列看成子串还真是对不起了。题目链接
? 首先由每两个数字出现的次数之差不超过1可以知道,以下几点。对于一个可以记入答案的序列,有
然后我们试着看一看k和答案的关系,假如设 add 为出现次数为 k+1 的数字个数,那么很显然的有 \(ans=8*k+add\).并且有 ans和k成正相关的关系。
? 有了以上的结论之后,我们可以二分猜k,然后试图计算出在完成每个数字至少出现k个的标准时,add的值。若是无法达到标准,那么就将k下调,否则上升。
? 现在只需要解决如何求add的问题。这里使用状压DP,\(f[x][j]\)表示在x的位置 j 所代表的数字都已经出现至少 k 次的add。然后需要一个辅助变量,\(pos[x][i]\)表示序列中第 i 个 x 的位置。这里用了一小点贪心的思想,即如果我们要选一些相同的数出来,那么这些数肯定是靠的越近越好,这样才能为后面的DP提供更多的机会。
? 那么转移方程可以比较清楚的推出,这里写不清楚,还是看代码会比较容易。需要注意的是,每次进行转移的时候,我们是先选出状态以外的一个数 p,然后得出这个 p 之后第 k-1 的 p的位置,转移到那上面去。(这样总共就有k个p),然后如果后面还剩的有 k 个 p(即总可塞入的有k+1个)那么也转移一下。
? 如何得到之后第 k-1 个 p 的位置是依靠于pos数组,但是每次不可能从\(pos[p][1]\) 开始往后面算,那么再用一个 start 数组表示应该是从 \(pos[p][start[p]]\) 开始找 k-1 个,即求出 \(pos[p][start[p]+k-1]\),注意\(start\)随着i而变化。
? 最后初始化\(f[0][0]=0\)就可以轻松解决掉这道题啦!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
#include<cmath>
#include<cstring>
using namespace std;
int read(){
char ch=getchar();
int res=0,f=1;
for(;ch<‘0‘||ch>‘9‘;ch=getchar())if(ch==‘-‘)f=-1;
for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar())res=res*10+(ch-‘0‘);
return res*f;
}
const int MAXN=(1<<8)-1;
int n,a[1005],t[9],pos[9][1005],r=1<<15,l;
int start[9],f[1005][MAXN+5],ans;
inline int maxx(int x,int y){
return x>y?x:y;
}
bool check(int x){
for(int i=1;i<=8;++i)start[i]=1;//初始化
memset(f,250,sizeof(f));
f[0][0]=0;
for(int i=0;i<n;++i){
for(int j=0;j<MAXN;++j)
if(f[i][j]>=0)//用它去更新
for(int k=1;k<=8;++k){//选一个数
if(j&(1<<(k-1)))continue;
int u=start[k]+x-1,g=j|(1<<(k-1));
if(u<=t[k])f[pos[k][u]][g]=maxx(f[pos[k][u]][g],f[i][j]);
if(++u<=t[k])f[pos[k][u]][g]=maxx(f[pos[k][u]][g],f[i][j]+1);//可以选出x+1个
}
++start[a[i]];//更新,每次选的数都要在i及其以后
}
int add=-1;
for(int i=8;i<=n;++i)add=maxx(add,f[i][MAXN]);//前7个不可能有答案
if(add==-1)return 0;
ans=maxx(ans,8*x+add);
return 1;
}
int main(){
n=read();
for(int i=1;i<=n;++i){
a[i]=read();
pos[a[i]][++t[a[i]]]=i;//记录位置
}
for(int i=1;i<=8;++i)r=min(r,t[i]);
if(r==0){//特判是否有数字没有,那么就是括号内的特殊情况
int res=0;
for(int i=1;i<=8;++i)if(t[i])res++;
cout<<res;
return 0;
}
while(l<=r){//二分猜k
int mid=(l+r)>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
printf("%d",ans);
return 0;
}
标签:and 状压dp add problem 注意 tar 图计算 特殊 辅助
原文地址:https://www.cnblogs.com/clockwhite/p/13406069.html