题目描述
她走的悄无声息,消失的无影无踪。
至今我还记得那一段时间,我们一起旅游,一起游遍山水。到了最终的景点,她却悄无声息地消失了,只剩我孤身而返。
现在我还记得,那个旅游区可以表示为一张由
个节点 条边组成无向图。我故地重游,却发现自己只想尽快地结束这次旅游。我从景区的出发点(即 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
", ans);
}
return 0;
}