对于一个连通块,取一个点进行dfs,得到一棵dfs搜索树,则这棵树的深度不超过10,且所有额外边都是前向边。
对于每个点x,设S为三进制状态,S第i位表示根到x路径上深度为i的点的状态:
0:选了
1:没选,且没满足
2:没选,且已满足
设f[i][j]表示考虑根到x路径上深度为i的点时这些点的状态为j时的最小费用,然后按DFS序进行DP即可。
时间复杂度$O((n+m)3^{10})$,空间复杂度$O(10 imes3^{10})$。
#include<cstdio> const int N=20010,M=50010,K=11,inf=2000000000; int n,m,i,x,y,a[N],g[N],v[M],nxt[M],ed,vis[N],d[N],q[K],pow[K],f[K][59050],ans; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline int bit(int x,int y){return x/pow[y]%3;} inline void up(int&x,int y){if(x>y)x=y;} inline int min(int x,int y){return x<y?x:y;} void dfs(int x,int y){ vis[x]=1,d[x]=y; if(!y)f[0][0]=a[x],f[0][1]=0,f[0][2]=inf; else{ int cnt=0; for(int i=g[x];i;i=nxt[i]){ int u=v[i]; if(vis[u]&&d[u]<y)q[cnt++]=d[u]; } for(int S=pow[y+1]-1;~S;S--)f[y][S]=inf; for(int S=pow[y]-1;~S;S--){ int U=1,V=S; for(int i=0;i<cnt;i++)if(bit(S,q[i])==0)U=2;else if(bit(S,q[i])==1)V+=pow[q[i]]; up(f[y][S+U*pow[y]],f[y-1][S]); up(f[y][V],f[y-1][S]+a[x]); } } for(int i=g[x];i;i=nxt[i]){ int u=v[i]; if(!vis[u]){ dfs(u,y+1); for(int S=0;S<pow[y+1];S++)f[y][S]=min(f[y+1][S],f[y+1][S+2*pow[y+1]]); } } } int main(){ for(pow[0]=i=1;i<K;i++)pow[i]=pow[i-1]*3; scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%d",&a[i]); while(m--)scanf("%d%d",&x,&y),add(x,y),add(y,x); for(i=1;i<=n;i++)if(!vis[i])dfs(i,0),ans+=min(f[0][0],f[0][2]); return printf("%d",ans),0; }