Senior Pan HDU - 6166 (两个集合的最短路)

Senior Pan

 HDU - 6166 

Senior Pan fails in his discrete math exam again. So he asks Master ZKC to give him graph theory problems everyday. 
The task is simple : ZKC will give Pan a directed graph every time, and selects some nodes from that graph, you can calculate the minimum distance of every pair of nodes chosen in these nodes and now ZKC only cares about the minimum among them. That is still too hard for poor Pan, so he asks you for help. 

Input

The first line contains one integer T, represents the number of Test Cases.1≤T≤5.Then T Test Cases, for each Test Cases, the first line contains two integers n,m representing the number of nodes and the number of edges.1≤n,m≤100000
Then m lines follow. Each line contains three integers xi,yixi,yi representing an edge, and vivi representing its length.1≤xi,yixi,yi≤n,1≤vivi≤100000 
Then one line contains one integer K, the number of nodes that Master Dong selects out.1≤K≤n 
The following line contains K unique integers aiai, the nodes that Master Dong selects out.1≤aiai≤n,aiai!=aj 

Output

For every Test Case, output one integer: the answer

Sample Input

1
5 6
1 2 1
2 3 3
3 1 3
2 5 1
2 4 2
4 3 1
3
1 3 5

Sample Output

Case #1: 2

题意:给出n个点m条边的有向图, 给出k个点,求这k个点两两之间的最短路的最小值。

 

https://www.cnblogs.com/Q1143316492/p/9293736.html

https://www.cnblogs.com/hua-dong/p/9824108.html

https://www.cnblogs.com/hua-dong/p/9824108.html

https://www.cnblogs.com/Q1143316492/p/9293736.html

思路:

多源多汇,比较容易想到建总源点和总汇点。之间跑源点汇点的最短路。但是这样问题很多。看来别人的操作后,随机把k个点分成两组。然后把两组分别连源点和汇点,权值0.一次最短路。如果答案的两个点被你分到了两个集合,那么答案就出来了。由于答案被分到两个集合的概率是0.5.做n次期望能有n/2次能获得答案。那么直接来个十几次随机。AC概率就很大了。 官方是按某个二进制位划分,因为来个数字不同,必有某位二进制是不同的。那么对二进制的每一位的0,1分集合就能包含所有划分集合。

这里分组可以随机20次;也可以用“二进制分组法”。:

举例说明,将1 2 3 4 进行分组,四个数的二进制形式为: 001 010 011 100 
第一次看第0位为1的数,那么A={1,3},B={2,4} 
第一次看第1位为1的数,那么A={2,3},B={1,4} 
第一次看第2位为1的数,那么A={4},B={1,2,3} 
可以看出,任意一对数字,都至少有一次机会不在同一组中。

1.从集合A中一点到集合B中一点的最短路径:设立源点0,连向所有A集合中的点,边权值为0;设立汇点,B集合中点向其连边,边权值为0。求源点到汇点的最短路即为集合A到集合B的最短路。

2.将最短两点分别分到A、B集合:由于图中任意两点标号不同,其二进制表示中必有至少一位不同。按二进制枚举第0--->17位,若u与v该位不同,则u分到集合A,v分到集合B,求A到B最短路和B到A最短路取最小。

3.由于不同u与v必定存在至少一位不同,所以每个u与每个v都两两分到不同集合(即包含所有组合)其中必有至少一次u与v分到正确集合,此时答案即为最小。
 

#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <string>
#include <cstdio>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <list>
#include <map>
#include <set>
using namespace std;
#define ll long long
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int M = 1e7 + 7;
const int N = 1e5 + 7;
int head[N], en;
struct Edge {
	int v, to, w;
} e[N<<1];
void adde(int u, int v, int w) {
	e[++en].v = v;
	e[en].w = w;
	e[en].to = head[u];
	head[u] = en;
}
int point[N], belong[N], in[N];
ll dis[N];
ll ans;
void init(int n) {
	memset(head, -1, sizeof(head)); en = 0;
}
queue<int> q;
void SPFA(int k) {
	memset(dis, 0x3f, sizeof(dis));
	memset(in, 0, sizeof(in));
	while (!q.empty()) q.pop();
	for (int i = 1; i <= k; ++i) {
		if (belong[point[i]] == 0) { //把0类点当作源点 
			q.push(point[i]);
			dis[point[i]] = 0; 
		}
	}
	while (!q.empty()) {
		int u = q.front();
		for (int i = head[u]; ~i; i = e[i].to) {
			int v = e[i].v, w = e[i].w;
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				if (!in[v]) {
					q.push(v);
					in[v] = 1;
				}
			}
		}
		q.pop();
		in[u] = 0;
	}
	for (int i = 1; i <= k; ++i) {
		if (belong[point[i]] == 1) { //1类点为汇点 
			ans = min(ans, dis[point[i]]);
		}
	}
}
int main() {
	int t, T = 0;
	scanf ("%d", &t);
	while (t--) {
		int n, m, k;
		scanf ("%d %d", &n, &m);
		init(n);
		int u, v, w;
		for (int i = 1; i <= m; ++i) {
			scanf ("%d %d %d", &u, &v, &w);
			adde(u, v, w);
		} 
		int max_bit = 0;
		k = n;
		while (k) ++max_bit, k >>= 1;
		scanf ("%d", &k);
		for (int i = 1; i <= k; ++i) {
			scanf ("%d", &point[i]);
		}
		ans = INF;
		for (int i = 0; i < max_bit; ++i) { //枚举每一位,按位分点 
			for (int j = 1; j <= k; ++j) { //把每个点分类 
				if (point[j] & (1<<i)) { 
					belong[point[j]] = 1;
				} else {
					belong[point[j]] = 0;
				}
			}
			SPFA(k);
		}
		printf ("Case #%d: %lld\n", ++T, ans);
	}
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值