Loading

那一天她离我而去(最短路 求最小环)

题目描述

她走的悄无声息,消失的无影无踪。
至今我还记得那一段时间,我们一起旅游,一起游遍山水。到了最终的景点,她却悄无声息地消失了,只剩我孤身而返。
现在我还记得,那个旅游区可以表示为一张由
个节点 条边组成无向图。我故地重游,却发现自己只想尽快地结束这次旅游。我从景区的出发点(即 1 号节点)出发,却只想找出最短的一条回路重新回到出发点,并且中途不重复经过任意一条边。
即:我想找出从出发点到出发点的小环。

输入格式

每个测试点有多组测试数据。
第一行有一个正整数T表示数据组数。
接下来对于每组数据,第一行有两个正整数n,m分别代表图的点数和边数。
接下来有m行,每行三个整数u,v,d表示u,v之间存在一条长度为d的路径。
保证不存在重边,自环。

输出格式

对于每组测试数据,输出题目中所求的最小环的长度。
无解输出-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\)
本题不卡spfa

solution

分三部分来说

  • 暴力dfs搜索从1出发的环中最小值,可以拿到\(30 \%\)
  • 枚举和1连接的每个点,把边权更改为inf然后跑最短路,跑完之后还原边权,继续跑下一个点
    这种写法在最一开始的最小环那个题是可以过的,但是后来加强了数据,只能拿到\(70 \%\)
  • 来考虑正解,对每一个点二进制拆分,显然每两个不同的数字在二进制下最少会有一位不同
    以每个二进制位的0,1进行分组,每组点组成的环一定被至少一次更新
    然后将相同的一组数连接根节点1,剩下的去连接超级源点\(n+1\)从1到跑最短路,每次用\(dis[n+1]\)更新\(ans\)即可

亲测spfa和dij均可过
对于随机数据spfa更快

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;
}

posted @ 2020-09-04 19:45  Gary_818  阅读(140)  评论(0编辑  收藏  举报