#最大独立集,状压dp#CF1767E Algebra Flash
分析
首先第一个颜色和第 \(n\) 个颜色一定要选,不妨建自环,
然后 \(i\) 只能跳到 \(i+1\) 和 \(i+2\) 说明相邻两个位置的颜色至少得选一个不然就无法跳过去
可以发现相当于最小点覆盖问题,而答案就转换成所有点权和减去最大权独立集的点权和
设 \(dp[S]\) 表示现在候选集合为 \(S\) 的最大权独立集,每次选编号最大的一个节点加进独立集,对于只含前一半节点的状态记忆化
也就是 \(dp[S]=\max(dp[S\backslash x],dp[S\backslash (x\cup N(x))]+a[x])\)
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long lll;
int n,m,col[300011],a[62]; lll ans,dp[1<<23],d[62];
void add(int x,int y){d[x]|=1ll<<y,d[y]|=1ll<<x;}
lll Dp(lll S){
if (S<(1<<23)&&~dp[S]) return dp[S];
lll x=63-__builtin_clzll(S),ans=Dp(S^(1ll<<x));
if (!(d[x]&(1ll<<x))) ans=max(ans,Dp(S^(1ll<<x)^(S&d[x]))+a[x]);
if (S<(1<<23)) dp[S]=ans;
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for (int i=1;i<=n;++i) cin>>col[i];
for (int i=0;i<m;++i) cin>>a[i],ans+=a[i];
add(col[1]-1,col[1]-1),add(col[n]-1,col[n]-1);
for (int i=2;i<n-1;++i) add(col[i]-1,col[i+1]-1);
memset(dp,-1,sizeof(dp));
dp[0]=0;
cout<<ans-Dp((1ll<<m)-1);
return 0;
}

浙公网安备 33010602011771号