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


    题目描述

    她走的悄无声息,消失的无影无踪。
    至今我还记得那一段时间,我们一起旅游,一起游遍山水。到了最终的景点,她却悄无声息地消失了,只剩我孤身而返。
    现在我还记得,那个旅游区可以表示为一张由
    个节点 条边组成无向图。我故地重游,却发现自己只想尽快地结束这次旅游。我从景区的出发点(即 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;
    }
    
    
  • 相关阅读:
    C#下给数字前面补0的方法
    Notepad++ xml 文件不能语法着色的问题解决
    excel 技巧
    编译时报警 implicit declaration of function
    配置ASP.NET平台时遇到的“访问IIS元数据库失败”解决方案
    用360安全卫士批量本地快速给系统打补丁【转贴】
    新雨情系统随笔
    我的开发博客开通了
    JQuery资料
    IGNORE_DUP_KEY = OF的作用
  • 原文地址:https://www.cnblogs.com/rui-4825/p/13615846.html
Copyright © 2020-2023  润新知