1. 程式人生 > 實用技巧 >那一天她離我而去 (最短路)

那一天她離我而去 (最短路)

(內部題目不放題面了) 題面

分析

  • 之前考過一道原題但是資料過水 所以暴力能過……
  • 先來說一下暴力的演算法 首先對於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;
}