bzoj5038 四叶草魔杖
很有意思的最小生成树啊。
网上的题解大多是状压+最小生成树,经过我的试验,其实只要把每个联通块找出来,一个个做一次就可以了。
放一个状压的。懒得再写一个搜索找联通块
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int n,m; struct edge{int x,y,d;}e[310]; bool cmp(edge e1,edge e2){return e1.d<e2.d;} int fa[110000]; int findfa(int x) { if(fa[x]==x)return x; fa[x]=findfa(fa[x]);return findfa(fa[x]); } int kruscal(int zt) { int cnt=0; for(int i=1;i<=n;i++) if(zt&(1<<i-1))fa[i]=i,cnt++; if(cnt==1)return 0; int ans=0; for(int i=1;i<=m;i++) if((zt&(1<<e[i].x-1))&&(zt&(1<<e[i].y-1))) { int fx=findfa(e[i].x),fy=findfa(e[i].y); if(fx!=fy) { fa[fx]=fy; ans+=e[i].d; cnt--;if(cnt==1)return ans; } } return (1<<30); } int c[30],sum[110000],f[110000]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&c[i]); for(int i=1;i<=m;i++) { scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].d),e[i].x++,e[i].y++; if(e[i].x==e[i].y)i--,m--; } sort(e+1,e+m+1,cmp); int maxp=(1<<n)-1; for(int zt=1;zt<=maxp;zt++) { sum[zt]=0; for(int i=1;i<=n;i++) if(zt&(1<<(i-1)))sum[zt]+=c[i]; if(sum[zt]==0)f[zt]=kruscal(zt); } for(int zt1=1;zt1<=maxp;zt1++) if(sum[zt1]==0&&f[zt1]!=(1<<30)) for(int zt2=zt1+1;zt2<=maxp;zt2++) if(sum[zt2]==0&&f[zt2]!=(1<<30)) if((!(zt1&zt2))) f[zt1|zt2]=min(f[zt1|zt2],f[zt1]+f[zt2]); if(sum[maxp]!=0||f[maxp]==(1<<30))printf("Impossible\n"); else printf("%d\n",f[maxp]); return 0; }
pain and happy in the cruel world.