#最大独立集,状压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;
}
posted @ 2026-08-14 10:42  lemondinosaur  阅读(7)  评论(0)    收藏  举报