BZOJ 4479: [Jsoi2013]吃货jyy
一句话题意:求必须包含某K条边的回路(回到1),使得总权值最小
转化为权值最小的联通的偶点
令F[i]表示联通状态为i的最小权值,(3^n状压)表示不在联通块内/奇点/偶点,连边时先不考虑必选的边的度数和权值
最后加上必须的边(保证必须的边都被选了)
连完这些边以后考虑剩下的一些奇点,两两配对,G[i]表示状态为i的奇点两两配对的代价(2^n状压)
#include<cstdio> #include<algorithm> using namespace std; int cnt,n,k,last[15],dis[15][15],G[10005],F[2000005],vis[15],a[15],mi[15]; struct node{ int to,next; }e[1000005]; void add(int a,int b){ e[++cnt].to=b; e[cnt].next=last[a]; last[a]=cnt; } int main(){ scanf("%d%d",&n,&k); for (int i=0; i<n; i++) for (int j=0; j<n; j++) dis[i][j]=1e9; int ans=0; for (int i=1; i<=k; i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); x--,y--; add(x,y); add(y,x); ans+=z; vis[x]++; vis[y]++; dis[x][y]=min(dis[x][y],z); dis[y][x]=min(dis[y][x],z); } int m; scanf("%d",&m); for (int i=1; i<=m; i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); x--,y--; dis[x][y]=min(dis[x][y],z); dis[y][x]=min(dis[y][x],z); } for (int k=0; k<n; k++) for (int i=0; i<n; i++) for (int j=0; j<n; j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); for (int i=0; i<(1<<n); i++) G[i]=1e9; G[0]=0; for (int i=0; i<(1<<n); i++) for (int x=0; x<n; x++) if (!(i&(1<<x))){ for (int y=x+1; y<n; y++) if (!(i&(1<<y))) G[i|(1<<x)|(1<<y)]=min(G[i|(1<<x)|(1<<y)],G[i]+dis[x][y]); } mi[0]=1; for (int i=1; i<=n; i++) mi[i]=mi[i-1]*3; for (int i=0; i<mi[n]; i++) F[i]=1e9; F[2]=0; for (int now=2; now<mi[n]; now++) if (F[now]!=1e9){ int N=0; for (int i=0; i<n; i++) if (now/mi[i]%3) a[N++]=i; for (int i=0; i<n; i++) if (!(now/mi[i]%3)){ for (int j=last[i]; j; j=e[j].next){ int V=e[j].to; if (now/mi[V]%3){ int To=now+mi[i]*2; F[To]=min(F[To],F[now]); } } for (int j=0; j<N; j++){ int To=now+mi[i]; if (now/mi[a[j]]%3==1) To+=mi[a[j]]; else if (now/mi[a[j]]%3==2) To-=mi[a[j]]; F[To]=min(F[To],F[now]+dis[i][a[j]]); } } } int ANS=1e9; for (int now=0; now<mi[n]; now++){ int flag=0,Now=now; for (int i=0; i<n; i++) if (vis[i] && !(Now/mi[i]%3)){ flag=1; break; } if (flag) continue; for (int i=0; i<n; i++) if (vis[i]&1){ if (Now/mi[i]%3==1) Now+=mi[i]; else if (Now/mi[i]%3==2) Now-=mi[i]; } int To=0; for (int i=0; i<n; i++) if (Now/mi[i]%3==1) To|=(1<<i); ANS=min(ANS,F[now]+G[To]); } printf("%d\n",ans+ANS); return 0; }