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

技术文章 1年前 (2020) 完美者
1,242 0

标签:n+1   pac   ems   init   ret   无法   line   int   数据   

(内部题目不放题面了) 题面

分析

  • 之前考过一道原题但是数据过水 所以暴力能过……
  • 先来说一下暴力的算法 首先对于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;
}

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

标签:n+1   pac   ems   init   ret   无法   line   int   数据   

原文地址:https://www.cnblogs.com/2004-08-20/p/13615893.html

版权声明:完美者 发表于 2020-09-17 13:28:41。
转载请注明:那一天她离我而去 (最短路) | 完美导航

暂无评论

暂无评论...