[欧拉回路][状压dp] Jzoj P3290 吃货JYY
题解
- 题目大意:给出k条必须走的路线,和m条选择走的路线,求一条经过k条必须走路径从1出发又回到1的路径(每条路都是双向的,只用给一份钱)
- 对于答案就是连通图里的一个子图,这个子图保证是欧拉图
- 首先,我们可以用Floyd求出任意两点的最短距离
- 然后可以用f[s],求出当前跑了的点的状态为s的最短距离
- 因为最后答案绝对是一个连通图
- 那么,可以把1先打进连通图
- 然后不断往连通图里添加点和边
- 对于一个欧拉图,任意一点的度都为偶数,不然不满足欧拉图的性质(有进无出)
- 那么可以用三进制压缩状态,0表示与连通图不相连,1表示度数为奇数,2表示度数为偶数
- 那么对于往连通图里加边,有两种情况:
- ①i与一条必须走的路径与连通图相连
- 因为这条边必须被经过一次,那么这条边不产生额外代价
- ②i与连通图里的一个点j直接相连,它们之间的距离就是两点之见的距离(预处理得出)
- 以上加点加边都要改变它们的奇偶性,也就是2——1,1——2
- 如果这时,求出了连通图的最小代价,其实不一定满足每一个点的度数都为奇数
- 那么可以将它们两两配对
- 将其最短距离加入图中
- 最后还要加上k条边的代价
- 就是answer
代码
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<queue> 6 using namespace std; 7 const int inf=0x3f3f3f3f; 8 queue<int>Q; 9 int n,m,dis[16][16],cnt,head[16],f[10000],g[1600005],t[16],mi[16],a[16],d[16]; 10 struct edge { int to,from,v; }e[100]; 11 void insert(int x,int y,int z) { e[++cnt].to=y; e[cnt].from=head[x]; e[cnt].v=z; head[x]=cnt; } 12 void floyd() 13 { 14 for (int k=1;k<=n;k++) 15 for (int i=1;i<=n;i++) 16 for (int j=1;j<=n;j++) 17 dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]); 18 } 19 void zy_dp() 20 { 21 memset(f,inf,sizeof(f)); 22 f[0]=0; 23 for (int i=0;i<mi[n];i++) 24 for (int j=1;j<=n;j++) 25 if (!(i&mi[j-1])) 26 for (int k=j+1;k<=n;k++) 27 if (!(i&mi[k-1])) 28 f[i^mi[j-1]^mi[k-1]]=min(f[i]+dis[j][k],f[i^mi[j-1]^mi[k-1]]); 29 } 30 void dp() 31 { 32 memset(g,inf,sizeof(g)); 33 g[2]=0; Q.push(2); 34 while (!Q.empty()) 35 { 36 int u=Q.front(),tot=0; Q.pop(); 37 for (int i=1;i<=n;i++) if (u/t[i-1]%3>0) a[++tot]=i; 38 for (int i=1;i<=n;i++) 39 if (u/t[i-1]%3==0) 40 { 41 for (int j=head[i];j;j=e[j].from) 42 if (u/t[e[j].to-1]%3>0) 43 { 44 int k=u+t[i-1]*2; 45 if (g[u]>=g[k]) continue; 46 if (g[k]==inf) Q.push(k); 47 g[k]=g[u]; 48 } 49 for (int j=1;j<=tot;j++) 50 { 51 int k=u+t[i-1]; 52 k+=(u/t[a[j]-1]%3==1)?t[a[j]-1]:-t[a[j]-1]; 53 if (g[u]+dis[i][a[j]]>=g[k]) continue; 54 if (g[k]==inf) Q.push(k); 55 g[k]=g[u]+dis[i][a[j]]; 56 } 57 } 58 } 59 } 60 void calc() 61 { 62 int ans=inf; 63 for (int i=0;i<t[n];i++) 64 { 65 int boo=0; 66 for (int j=1;j<=n;j++) if (head[j]&&!((i/t[j-1])%3)) { boo=1; break; } 67 if (boo) continue; 68 int k=i; 69 for (int j=1;j<=n;j++) if (d[j]%2==1) if (i/t[j-1]%3==1) k+=t[j-1]; else k-=t[j-1]; 70 int w=0; 71 for (int j=1;j<=n;j++) if (k/t[j-1]%3==1) w^=mi[j-1]; 72 ans=min(ans,g[i]+f[w]); 73 } 74 for (int i=1;i<=cnt;i+=2) ans+=e[i].v; 75 printf("%d",ans); 76 } 77 int main() 78 { 79 memset(dis,inf,sizeof(dis)); 80 scanf("%d%d",&n,&m); 81 mi[0]=t[0]=1; 82 for (int i=1;i<=n;i++) mi[i]=mi[i-1]*2,t[i]=t[i-1]*3; 83 for (int i=1;i<=m;i++) 84 { 85 int x,y,z; 86 scanf("%d%d%d",&x,&y,&z); 87 dis[x][y]=dis[y][x]=min(dis[x][y],z); 88 d[x]++; d[y]++; 89 insert(x,y,z); insert(y,x,z); 90 } 91 scanf("%d",&m); 92 for (int i=1;i<=m;i++) 93 { 94 int x,y,z; 95 scanf("%d%d%d",&x,&y,&z); 96 dis[x][y]=dis[y][x]=min(dis[x][y],z); 97 } 98 floyd(); zy_dp(); dp(); calc(); 99 return 0; 100 }