tyvj2054 四叶草魔杖——连通块 & 状压DP
题目:http://www.joyoi.cn/problem/tyvj-2054
把点分成几个连通块,和为0的几个点放在一块,在块内跑最小生成树作为这个块的代价;
然后状压DP,组成全集的最小代价就是答案;
1A了好高兴!
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,a[20],hd[20],ct,f[1<<20],cnt,fa[20],inf=0x3f3f3f3f,g[1<<20],tot; bool vis[20]; struct N{ int u,v,w; N(int t=0,int n=0,int w=0):u(t),v(n),w(w) {} }e[205]; bool cmp(N x,N y){return x.w<y.w;} int calc(int g) { int sum=0; for(int i=1;i<=n;i++) if(g&(1<<(i-1)))sum+=a[i]; return sum; } int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} int kruskal(int g) { int num=0,ret=0; memset(vis,0,sizeof vis); for(int i=1;i<=n;i++) { fa[i]=i; if(g&(1<<(i-1)))vis[i]=1,num++; } int t=0; for(int i=1;i<=m;i++) { if(!vis[e[i].u]||!vis[e[i].v])continue; int u=find(e[i].u),v=find(e[i].v); if(u!=v) { fa[u]=v; ret+=e[i].w; t++; if(t==num-1)break; } } if(t!=num-1)return inf; return ret; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w),e[i].u++,e[i].v++; sort(e+1,e+m+1,cmp); memset(f,0x3f,sizeof f); for(int i=1;i<=(1<<n)-1;i++) if(calc(i)==0) { int ff=kruskal(i); if(ff!=inf)g[++tot]=i,f[i]=ff; } f[0]=0; for(int i=0;i<=(1<<n)-1;i++) { for(int j=1;j<=tot;j++) if((i&g[j])==0)f[i|g[j]]=min(f[i|g[j]],f[i]+f[g[j]]); } if(f[(1<<n)-1]==inf)printf("Impossible\n"); else printf("%d\n",f[(1<<n)-1]); return 0; }