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

題目描述

她走的悄無聲息,消失的無影無蹤。
至今我還記得那一段時間,咱們一塊兒旅遊,一塊兒遊遍山水。到了最終的景點,她卻悄無聲息地消失了,只剩我孤身而返。
如今我還記得,那個旅遊區能夠表示爲一張由
個節點 條邊組成無向圖。我故地重遊,卻發現本身只想儘快地結束此次旅遊。我從景區的出發點(即 1 號節點)出發,卻只想找出最短的一條迴路從新回到出發點,而且中途不重複通過任意一條邊。
即:我想找出從出發點到出發點的小環。node

輸入格式

每一個測試點有多組測試數據。
第一行有一個正整數T表示數據組數。
接下來對於每組數據,第一行有兩個正整數n,m分別表明圖的點數和邊數。
接下來有m行,每行三個整數u,v,d表示u,v之間存在一條長度爲d的路徑。
保證不存在重邊,自環。ios

輸出格式

對於每組測試數據,輸出題目中所求的最小環的長度。
無解輸出-1。測試

樣例輸入

2
3 3
1 2 1
2 3 1
3 1 1
4 5
1 2 2
2 3 2
3 4 2
1 4 2
1 3 5

樣例輸出

3
8

數據範圍與提示

對於100%的數據:
\(n⩽10^4\)
\(m⩽4×10^4\)
本題不卡spfaspa

solution

分三部分來講code

  • 暴力dfs搜索從1出發的環中最小值,能夠拿到\(30 \%\)
  • 枚舉和1鏈接的每一個點,把邊權更改成inf而後跑最短路,跑完以後還原邊權,繼續跑下一個點
    這種寫法在最一開始的最小環那個題是能夠過的,可是後來增強了數據,只能拿到\(70 \%\)
  • 來考慮正解,對每個點二進制拆分,顯然每兩個不一樣的數字在二進制下最少會有一位不一樣
    以每一個二進制位的0,1進行分組,每組點組成的環必定被至少一次更新
    而後將相同的一組數鏈接根節點1,剩下的去鏈接超級源點\(n+1\)從1到跑最短路,每次用\(dis[n+1]\)更新\(ans\)便可

親測spfa和dij都可過
對於隨機數據spfa更快get

code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

inline int read(){
	int x = 0, w = 1;
	char ch = getchar();
	for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

const int ss = 100005;

struct e{
	int to, nxt, w;
}edge[ss << 1];

int head[ss << 1], tot;

inline void add(register int u, register int v, register int w){
	edge[++tot].to = v;
	edge[tot].nxt = head[u];
	edge[tot].w = w;
	head[u] = tot;
}

queue<int> q;
bool vis[ss];
int dis[ss], point[ss], w[ss], num[ss];

inline void spfa(register int s){
	memset(dis, 0x3f, sizeof dis);
	memset(vis, 0, sizeof vis);
	dis[s] = 0;
	q.push(s);
	while(!q.empty()){
		register int u = q.front();
		q.pop();
		vis[u] = 0;
		for(register int i = head[u]; i; i = edge[i].nxt){
			register int v = edge[i].to;
			if(dis[v] > dis[u] + edge[i].w){
				dis[v] = dis[u] + edge[i].w;
				if(!vis[v]){
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}
}

struct node{
	int to, val;
}a[ss];
int cnt;
int tmp[ss << 1];

inline int min(register int a, register int b){
	return a < b ? a : b;
}

inline void swap(register int &a, register int &b){
	int tmp = b;
	b = a;
	a = tmp;
}

signed main(){
	freopen("leave.in", "r", stdin);
	freopen("leave.out", "w", stdout);
	register int T = read();
	while(T--){
		memset(head, 0, sizeof head);
		memset(vis, 0, sizeof vis);
		memset(dis, 0x3f, sizeof dis);
		tot = cnt = 0;
		register int n = read(), m = read();
		for(register int i = 1; i <= m; i++){
			register int u = read(), v = read(), w = read();
			if(u > v) swap(u, v);
			if(u == 1) a[++cnt].to = v, a[cnt].val = w;
			else add(u, v, w), add(v, u, w);
		}
		register int ans = 0x3f3f3f3f;
		memcpy(tmp, head, sizeof head);
		for(register int i = 0; (1 << i) <= cnt; i++){
			memcpy(head, tmp, sizeof tmp);
			for(register int j = 1; j <= cnt; j++){
				if(j & (1 << i)) add(1, a[j].to, a[j].val);
				else add(a[j].to, n + 1, a[j].val);
			}
			spfa(1);
			ans = min(ans, dis[n + 1]);
		}
		if(ans == 0x3f3f3f3f) ans = -1;
		printf("%d\n", ans);
	}
	return 0;
}
相關文章
相關標籤/搜索