1. 程式人生 > >tyvj2054 四葉草魔杖——連通塊 & 狀壓DP

tyvj2054 四葉草魔杖——連通塊 & 狀壓DP

最小生成樹 can target kruskal problem ref 答案 get style

題目: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; }

tyvj2054 四葉草魔杖——連通塊 & 狀壓DP