BZOJ3590 SNOI2013Quare(状压dp)
可能作为最优解的边双都可以这样生成:初始时边双内只有一个点,每次选取边双内部两点(可以相同)和一个当前不在边双内的点集,以该两点为起止点找一条链(当然如果两点相同就是个环)将点集串起来,加入边双。状压dp模拟这个过程即可。注意找链时对二元环特判。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 12 #define M 42 #define inf 100000000 #define rep(i,t,S) for (int t=S,i=lg2[t&-t];t;t^=t&-t,i=lg2[t&-t]) char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,a[N][N],f[1<<N],g[N][N][1<<N],lg2[1<<N]; struct data{int x,y,z; }edge[M]; int main() { #ifndef ONLINE_JUDGE freopen("bzoj3590.in","r",stdin); freopen("bzoj3590.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif int T=read(); while (T--) { n=read(),m=read(); for (int i=1;i<=m;i++) edge[i].x=read()-1,edge[i].y=read()-1,edge[i].z=read(); memset(f,42,sizeof(f));for (int i=0;i<n;i++) f[1<<i]=0,lg2[1<<i]=i; memset(g,42,sizeof(g)); for (int i=0;i<n;i++) for (int j=0;j<n;j++) for (int k=1;k<=m;k++) if (edge[k].x==i&&edge[k].y==j||edge[k].x==j&&edge[k].y==i) g[i][j][0]=min(g[i][j][0],edge[k].z); for (int i=1;i<(1<<n);i++) { rep(x,p,(1<<n)-1^i) rep(y,q,(1<<n)-1^i) if (x!=y||i!=(i&-i)) rep(j,o,i) g[x][y][i]=min(g[x][y][i],g[j][y][i^(1<<j)]+g[j][x][0]); else { int mn=inf,mn2=inf; for (int j=1;j<=m;j++) if (edge[j].x==x&&edge[j].y==lg2[i]||edge[j].x==lg2[i]&&edge[j].y==x) if (edge[j].z<mn) mn2=mn,mn=edge[j].z; else if (edge[j].z<mn2) mn2=edge[j].z; g[x][y][i]=mn+mn2; } } for (int i=1;i<(1<<n);i++) for (int j=i-1&i;j;j=j-1&i) rep(x,u,i^j) rep(y,v,i^j) { f[i]=min(f[i],f[i^j]+g[x][y][j]); if (x==y) break; } if (f[(1<<n)-1]<inf) printf("%d\n",f[(1<<n)-1]); else printf("impossible\n"); } return 0; }