HDU 6166 2017多校 Team09 1006:Dijkstra顶点子集最短路径

宕掉了好几天。。。。。来发一个水题的题解

题意:给出一个有向图(n,m<=1e5),并给出一个询问集合,请求出集合中的点,两两之间的最短距离。

题解:回想最短路算法,首先排除掉N^3的的那个,然后剩下SPFA和Dijkstra跑多次的复杂度比较能接受,这两个其实是差不多的东西,由于边权都是正的,就上Dijkstra吧。

基础版的Dijkstra是单源多汇的,但是本题是多源多汇,但是Dij他是单源的……等等。。。Dij也可以多源呀,只要开一个超源0,用长度为0的边连接到各个起点,在把每个终点用长度为0的边连接到超汇n+1,这样0 – n+1的最短路就是从所有的起点到所有的终点路径中最短的。那么我们要想办法把真正最短答案的起点 分到起点集合中,把真正的最优终点放到终点集合,其他的随便放哪里都行。emmmm随机化算法随机分组。。。期望做4次可以得到正确答案。。。。

官解:按照点的标号的每个二进制位分组,最多分20次(准确的说是17次)。每次会把某一位不同的点分开到起点和终点集,然后再起点终点互换,再做一次。

正确性在于:对于任意两个点u和v,u和v是不同的点,必然有至少一个位不同,因此至少有一次他们被分到了各自正确的集合中,得到了正确答案,其他的答案都比他要大。

注意:这个题好像卡了vector的常数。。。模拟链表可以过掉。但是好像大家都是随机化算法搞得。。。

拓展:如果题目中没有环,还有另外一种哦做法:对于每个询问点x,连接0->x长度为0的边,对于每个点 i ,如果i有连接到某个询问点的边,那么把这条边重定向到 n+1点,从0到n+1跑一次就是答案。这个题的话。。。因为环路的存在。。。所以诸如1->2->3->1这样的道路也被统计到了。。。就得到了非法答案。。。。我也想不到什么好的方法。。。只好放弃了。。

PS:上面这个思路是我十分钟极限操作出来的。写的时候就担心有环。。最后果然WA了。

Code:

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;

#define N 100010
#define INF 0x3f3f3f3f3f3f3f3fLL
#define LL long long
#define p E[i].x
#define bit(x) (1<<(x))

using namespace std;

struct node
{
	int x;
	LL v;
	bool operator<(const node &tmp)const
	{
		return v>tmp.v;
	}
};

priority_queue<node> q;

struct edge
{
	int x,to,v;
}E[N<<1];

int n,m,totE,g[N],X[N],Y[N],Z[N],a[N];
LL dist[N];
bool v[N];

void addedge(int x,int y,int v)
{
	E[++totE] = (edge){y,g[x],v}; g[x] = totE;
}

LL calc_dist(int S,int T)
{
	for(int i=0;i<=n+1;i++) dist[i] = INF, v[i] = 0;
	dist[S]=0;
	q.push((node){S,0});
	node tmp;
	while(!q.empty())
	{
		tmp = q.top(); q.pop();
		int x = tmp.x;
		if(v[x]) continue;
		v[x] = 1;
		for(int i=g[x];i;i=E[i].to)
			if(!v[p] && dist[p]>dist[x]+E[i].v)
			{
				dist[p] = dist[x] + E[i].v;
				q.push((node){p,dist[p]});
			}
	}
	return dist[T];
}

int main()
{
    //freopen("in0.txt","r",stdin);
	int T,K,Te = 0;
	cin>>T;
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++) scanf("%d%d%d",&X[i],&Y[i],&Z[i]);
		scanf("%d",&K);
		for(int i=1;i<=K;i++) scanf("%d",&a[i]);
		LL ans = INF;
		for(int t=0;t<20;t++)
		{
			totE = 0;
			for(int i=0;i<=n+1;i++) g[i] = 0;
			for(int i=1;i<=m;i++) addedge(X[i],Y[i],Z[i]);
			for(int i=1;i<=K;i++)
			{
				if(a[i]&bit(t)) addedge(0,a[i],0);
				else addedge(a[i],n+1,0);
			}
			ans = min(ans, calc_dist(0,n+1));
			totE = 0;
			for(int i=0;i<=n+1;i++) g[i] = 0;
			for(int i=1;i<=m;i++) addedge(X[i],Y[i],Z[i]);
			for(int i=1;i<=K;i++)
			{
				if((a[i]&bit(t))==0) addedge(0,a[i],0);
				else addedge(a[i],n+1,0);
			}
			ans = min(ans, calc_dist(0,n+1));
		}
		//assert( ans < INF );
		printf("Case #%d: %lld\n",++Te,ans);
	}
	return 0;
}
    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/calabash_boy/article/details/77487605
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞