【洛谷P6085】吃貨 JYY
阿新 • • 發佈:2021-07-17
題目
題目連結:https://www.luogu.com.cn/problem/P6085
世界上一共有 \(N\) 個 JYY 願意去的城市,分別從 \(1\) 編號到 \(N\)。JYY 選出了 \(K\) 個他一定要乘坐的航班。除此之外,還有 \(M\) 個 JYY 沒有特別的偏好,可以乘坐也可以不乘坐的航班。
一個航班我們用一個三元組 \((x,y,z)\) 來表示,意義是這趟航班連線城市 \(x\) 和 \(y\),並且機票費用是 \(z\)。每個航班都是往返的,所以 JYY 花費 \(z\) 的錢,既可以選擇從 \(x\) 飛往 \(y\),也可以選擇從 \(y\) 飛往 \(x\)。
南京的編號是 \(1\)
\(n\leq 13,k\leq 78,m\leq 200\)。
思路
最終選出的肯定是一個迴路。所以我們可以把每個點度數的奇偶性狀壓一下進行 dp。
設 \(f[s]\) 表示狀態為 \(s\) 時的最小代價。對於每一個點,\(0\) 表示不在當前連通塊中,\(1\) 表示度數為奇數,\(2\) 表示度數為偶數。把這個三進位制狀壓起來當做狀態 \(s\)。
轉移的話就列舉在連通塊內的一個點 \(i\),以及一個連通塊外的點 \(j\),然後考慮把 \(i\) 和 \(j\) 連起來。
如果 \(i,j\)
統計答案需要再預處理出 \(g[s]\) 表示集合 \(s\) 內的點度數是奇數,把他們兩兩連線的最小代價。注意這裡的 \(s\) 是二進位制。
最後列舉每一種三進位制的狀態,把其中度數為奇數的點拿出來加上 \(g\) 的貢獻,取最小值後加上所有必須走的邊的長度即可。
時間複雜度 \(O(n^2(3^n+2^n))\)。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=15,M=1594330,Inf=1e9; int n,m1,m2,ans,sum,S,dis[N][N],pw[N],f[M],g[M]; bool v[N][N],vis[N]; queue<int> q; int main() { scanf("%d%d",&n,&m1); memset(dis,0x3f3f3f3f,sizeof(dis)); for (int i=1,x,y,z;i<=m1;i++) { scanf("%d%d%d",&x,&y,&z); dis[x][y]=dis[y][x]=min(dis[x][y],z); sum+=z; v[x][y]=v[y][x]=vis[x]=vis[y]=1; S^=(1<<x-1)^(1<<y-1); } vis[1]=1; scanf("%d",&m2); for (int i=1,x,y,z;i<=m2;i++) { scanf("%d%d%d",&x,&y,&z); dis[x][y]=dis[y][x]=min(dis[x][y],z); } for (int k=1;k<=n;k++) for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (i!=j && j!=k && i!=k) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); memset(f,0x3f3f3f3f,sizeof(f)); pw[0]=1; for (int i=1;i<=n;i++) { pw[i]=pw[i-1]*3; f[pw[i-1]*2]=0; q.push(pw[i-1]*2); } while (q.size()) { int s=q.front(); q.pop(); for (int j=1,ss;j<=n;j++) if (!((s/pw[j-1])%3)) for (int i=1;i<=n;i++) if ((s/pw[i-1])%3) if (!v[i][j]) { if ((s/pw[i-1])%3==1) ss=s+pw[j-1]+pw[i-1]; else ss=s+pw[j-1]-pw[i-1]; if (f[ss]>Inf) q.push(ss); f[ss]=min(f[ss],f[s]+dis[i][j]); } else { ss=s+2*pw[j-1]; if (f[ss]>Inf) q.push(ss); f[ss]=min(f[ss],f[s]); } } memset(g,0x3f3f3f3f,sizeof(g)); g[0]=0; ans=Inf; for (int s=0;s<(1<<n);s++) for (int i=1;i<=n;i++) if (s&(1<<i-1)) for (int j=i+1;j<=n;j++) if (s&(1<<j-1)) g[s]=min(g[s],g[s^(1<<i-1)^(1<<j-1)]+dis[i][j]); for (int s=0;s<pw[n];s++) { int ss=0; bool flag=1; for (int i=0;i<n;i++) { if ((s/pw[i])%3==1) ss+=(1<<i); if (vis[i+1] && !((s/pw[i])%3)) { flag=0; break; } } if (flag) ans=min(ans,f[s]+g[ss^S]); } cout<<ans+sum; return 0; }