[spfa][最小生成树] Jzoj P4261 最小代价

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

题解

  • 一开始时,一直在想如果找到与哪个黑点相连
  • 可以考虑一下从建一个S源点,然后把黑点向S点连一条权值为0的边
  • 然后我们就可以求出从S点到所有点的最短距离
  • 问题就转换为了求白点到S的距离
  • 从S出发往回dfs,找到最短路径上的边
  • 一条边可能被多次经过,我们dfs可能会重复把边选出来,但题目要求一条边只加一次代价
  • 所以可以用Kruskal,把边连起来,避免重复边
  • 如果我们求的边权和ans==0,那么说明一条边也找不到,那么“impossible”

代码

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 using namespace std;
 8 const int inf=0x3f3f3f;
 9 struct edge {long long to,from,v; }e[300010*2],k[300010*2];
10 long long dis[100010],ans,fa[100010],n,m,head[100010],cnt,tot;
11 bool f[100010];
12 queue<int>Q;
13 void insert(int x,int y,int z) { e[++cnt].to=y; e[cnt].from=head[x]; e[cnt].v=z; head[x]=cnt; }
14 void spfa()
15 {
16     memset(dis,inf,sizeof(dis));
17     memset(f,false,sizeof(f));
18     Q.push(0); dis[0]=0;
19     while (!Q.empty())
20     {
21         int u=Q.front(); Q.pop();
22         f[u]=false;
23         for (int i=head[u];i;i=e[i].from)
24             if (dis[u]+e[i].v<dis[e[i].to])
25             {
26                 dis[e[i].to]=dis[u]+e[i].v;
27                 if (!f[e[i].to])
28                 {
29                     Q.push(e[i].to);
30                     f[e[i].to]=true;
31                 }
32             }
33     }
34 }
35 void dg(int x)
36 {
37     for (long long i=head[x];i;i=e[i].from)
38         if (dis[x]+e[i].v==dis[e[i].to])
39         {
40             dg(e[i].to);
41             k[++tot].from=x,k[tot].to=e[i].to,k[tot].v=e[i].v;
42         }
43 }
44 long long getfather(int x)
45 {
46     if (fa[x]==x) return x;
47     return fa[x]=getfather(fa[x]);
48 }
49 void kruskal()
50 {
51     for (long long i=1;i<=tot;i++)
52     {
53         long long x=getfather(k[i].from),y=getfather(k[i].to);
54         if (x!=y)
55         {
56             ans+=k[i].v;
57             fa[y]=x;
58         }
59     }
60 }
61 bool cmp(edge x,edge y) { return x.from<y.from; }
62 int main()
63 {
64     scanf("%lld%lld",&n,&m);
65     for (int i=1;i<=n;i++)
66     {
67         int x;
68         scanf("%d",&x);
69         if (x==1) insert(0,i,0);
70     }
71     for (int i=1;i<=m;i++)
72     {
73         int x,y,z;
74         scanf("%d%d%d",&x,&y,&z);
75         insert(x,y,z); insert(y,x,z);
76     }
77     spfa();
78     dg(0);
79     sort(k+1,k+tot+1,cmp);
80     for (int i=1;i<=n;i++) fa[i]=i;
81     kruskal();
82     if (ans==0) printf("impossible"); else printf("%lld",ans);
83 }
原文地址:https://www.cnblogs.com/Comfortable/p/9296326.html