【NOIP2015模拟10.22】最小代价题解

Description

给出一幅由n个点m条边构成的无向带权图。
其中有些点是黑点,其他点是白点。
现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个黑点,可以选取其中任意一个),我们想要使得花费的代价最小。请问这个最小代价是多少?
注意:最后选出的边保证每个白点到离它最近的黑点的距离仍然等于原图中的最短距离。

Input

第一行两个整数n,m;
第二行n 个整数,0表示白点,1 表示黑点;
接下来m 行,每行三个整数x,y,z,表示一条连接x和y 点,权值为z 的边。

Output

如果无解,输出impossible;
否则,输出最小代价。

Sample Input

5 7
0 1 0 1 0
1 2 11
1 3 1
1 5 17
2 3 1
3 5 18
4 5 3
2 4 5

Sample Output

5
【样例解释】
选 2、4、6三条边

Data constraint

对30%的输入数据: 1≤n≤10, 1≤m≤20;
对100%的输入数据:1≤n≤100000,1≤m≤200000,1≤z≤1000000000

Solution

首先,我们新建一个源点,连接所有黑点,边权为0,然后跑一遍最短路,即可求出每一个白点到黑点的最短距离。
然后,利用贪心的思想。我们扫一遍每一个白点,在扫一遍每一条直接连向它的边。对于一条边,如果是从当前的白点走过去,也就是dis[i]==dis[j]+len那么我们就要找到一条最短的这样的边,并加入答案,其它长的边全部删掉。如果是从对面走到当前这一个白点,则不做任何操作。

Code

#include
#include
#include
#include
#define ll long long
using namespace std;
const int N=100001;
int n,m,cnt=0,sum=0,sum1=0;
bool a[N],vis[N];
int head[N],b[N],white[N];
ll dis[N],ans=0;
struct node {
	int to,next,len;
}edge[500001];
void add(int x,int y,int len) {//链式前向星 
	edge[cnt].to=y;
	edge[cnt].len=len;
	edge[cnt].next=head[x];
	head[x]=cnt++;
}
queue <int> q;
void spfa() {//最短路 
	for(int j=1;j<=n;j++) {
		dis[j]=1e17;
		vis[j]=0;
	}
	q.push(0);vis[0]=1;dis[0]=0;
	while(!q.empty()) {
		int now=q.front();
		q.pop();
		for(int j=head[now];j!=-1;j=edge[j].next) {
			int to=edge[j].to,len=edge[j].len;
			if (dis[now]+len<dis[to]) {
				dis[to]=dis[now]+len;
				if(!vis[to]) {
					vis[to]=1;
					q.push(to);
				}
			}
		}
		vis[now]=0;
	}
}
int main() {
	scanf("%d%d",&n,&m);
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		if(!a[i])white[++sum]=i;
		if(a[i])add(0,i,0);
	}
	while(m--) {
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		if(a[u]&&a[v])continue;
		add(u,v,w);add(v,u,w);
	}
	spfa();
	for(int i=1;i<=n;i++)
		if(dis[i]==1e17) {
			puts("impossible");
			return 0;
		}
	for(int i=1;i<=sum;i++) {//贪心,扫每一个白点 
		int k=1e9+5;
		for(int j=head[white[i]];j!=-1;j=edge[j].next) {
			int to=edge[j].to;
			int len=edge[j].len;
			if(dis[white[i]]==dis[to]+len||a[to])//判断是否是从当前点走过去 
				if(len<k)k=len;//求最短边 
		}
		ans+=k;
	}
	cout<<ans;
}

你可能感兴趣的:(贪心,最短路)