[spfa][最小生成树] Jzoj P4261 最小代价
题解
- 一开始时,一直在想如果找到与哪个黑点相连
- 可以考虑一下从建一个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 }