那一天她離我而去 (最短路)
阿新 • • 發佈:2020-09-04
(內部題目不放題面了) 題面
分析
- 之前考過一道原題但是資料過水 所以暴力能過……
- 先來說一下暴力的演算法 首先對於1號節點 如果1號節點的入度為1 那麼肯定無法回到1號節點 輸出-1 最小環可以看成是從1號節點出發 然後經過一號節點的某一個可到達位置 再去一號節點的另一個可到達位置 然後加上這兩個到1號節點的路徑長 所以我們可以列舉一遍1號節點可到達的所有點 然後每次都跑一遍最短路 更新答案
- 但是暴力顯然最壞的情況會退化成O(\(n^2\)\(log_n\)) 顯然是過不去這個題的 我們可以考慮分組 然後跑最短路
- 如果我們把1可以到達的一些點分為一組 另i一些點分為另外一組 然後建立超級源點向第一組的所有點都加邊 權值為1到達該點的路徑長 建立超級匯點向另外一組的所有點都加邊 權值也為1到達該點的路徑長 顯然答案就是超級源點到超級匯點的最短路
- 接下來就是我們如何不漏情況的分組 對於兩個點 我們必須令其至少一次在不同的兩組中 這樣才可能計算它對於答案的貢獻 所以可以使用二進位制分組 因為對於兩個節點 編號肯定不同 編號不同則編號化成二進位制後 也至少有一位是不同的 所以我們可以列舉每個二進位制位 然後將該位是1的分成一組 該位是0的分成一組 這樣可以保證不會漏掉答案
- 實現細節挺多的
Code
#include<bits/stdc++.h> using namespace std; #define rint register int const int maxn = 2e4 + 10; int head[maxn << 3]; int Head[maxn << 3]; int cnt; int fz[] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384}; int ans; int dis[maxn]; int vis[maxn]; struct node{ int next,to,dis; }a[maxn << 3]; void add(int x,int y,int z){ a[++cnt].next = head[x]; a[cnt].to = y; a[cnt].dis = z; head[x] = cnt; } void init(){ cnt = 0; ans = INT_MAX; memset(head,0,sizeof(head)); } deque<int> q; void spfa(int s){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[s] = 0; vis[1] = 1; q.push_front(s); while(!q.empty()){ rint u = q.front(); q.pop_front(); vis[u] = 0; for(rint i = head[u];i;i = a[i].next){ rint v = a[i].to; if(dis[v] > dis[u] + a[i].dis){ dis[v] = dis[u] + a[i].dis; if(!vis[v]) { if(q.empty()) q.push_front(v); else if(dis[v] < dis[q.front()]) q.push_front(v); else q.push_back(v); } vis[v] = 1; } } } } int main(){ freopen("leave.in","r",stdin); freopen("leave.out","w",stdout); int T;scanf("%d",&T); while(T--){ init(); int n,m;scanf("%d%d",&n,&m); for(rint i = 1;i <= m;++i){ int u,v,z;scanf("%d%d%d",&u,&v,&z); add(u,v,z);add(v,u,z); } for(rint i = head[1];i;i = a[i].next) Head[a[i].to] = head[a[i].to]; rint now = cnt; for(rint i = 0;n >> i;++i){ cnt = now; for(rint j = head[1];j;j = a[j].next){ rint v = a[j].to; if(v & (1 << i)) add(n + 1,v,a[j].dis); else add(v,n + 2,a[j].dis); } spfa(n+1); ans = min(ans,dis[n+2]); for(rint j = head[1];j;j = a[j].next) head[a[j].to] = Head[a[j].to]; head[n + 1] = head[n + 2] = 0; } if(ans != 0x3f3f3f3f) cout << ans << endl; else cout << "-1" << endl; } return 0; }