题目链接: 点击打开链接
3 1 0 1 1 4 4 0 1 10 0 2 10 1 3 20 2 3 30
impossible 40 0
题意:n,m分别代表有n个城市,m条边,然后是s,t,c代表一条从s出发到t的边权值是c。问你求一个城市,这个城市到其他所有城市的总和最小
思路:因为是有向图,很容易想到最小树形图,不过无根要怎么处理呢? 实际上我们只要假设有一个超级源点0号点,在原有边的基础上,再从0号点到各个点拉一条边,0号点到其他所有点的距离是所有边的总和+1(为什么要+1后面解释) 然后以超级源点为起点对新图求一个最小树形图。
又因为从最小树形图里超级源点到其他所有点里有且仅能有一条边,我们可以有反证法,如果存在两条从超级源点出发的边,那么由于从超级源点出发的边的长度比所有边都要长,而在已经有一条从超级源点出发的边的基础上,第二个点仍然要选择超级源点,那么我们可以确定,第二个点是单独出来的点,不跟其他任何边有连接或者图里存在两个入度为0的点(这两种情况都是不可能存在最小树形图的情况)
懂了这个我们就可以求出有超级源点的情况下最小树形图的最大长度(上界)是多少,假设题目给的边都在最小树形图里,那么此时达到最大长度,是sum+sum-1
因此如果最小树形图的长度>=2*sum,那么就不满足情况了。
注意sum不能不加一,我们可以假设存在一种情况有两个点,0条边,那么此时会有两条从超级源点出发的长度为0的边,那么此时无法判断是不是满足条件(0>2*0不满足,会认为是满足条件的,实际上0条边是不满足条件的)
所以排除掉不成立情况,最后结果就是有超级源点的情况下最小树形图的长度减去sum
代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define N 1010 #define INF 0x7f7f7f7f struct Edge { int u,v,w; } e[N*N]; int cnt; int in[N]; int vis[N],pre[N],id[N]; int minroot; void addedge(int u,int v,int w) { e[cnt].u=u; e[cnt].v=v; e[cnt++].w=w; } int Directed_MST(int root,int NV,int NE) { int ret = 0; while(true) { ///步骤1:找到最小边 for(int i = 0; i < NV; i ++) in[i] = INF; memset(pre,-1,sizeof(pre)); for(int i = 0; i < NE; i ++) { int u = e[i].u , v = e[i].v; if(e[i].w < in[v] && u != v) { pre[v] = u; in[v] = e[i].w; if(u==root) minroot=i; } } for(int i = 0; i < NV; i ++) { if(i == root) continue; if(in[i] == INF) return -1;///除了根节点以外有点没有入边,则根无法到达他 } int cntnode = 0; memset(id,-1,sizeof(id)); memset(vis,-1,sizeof(vis)); ///找环 in[root] = 0; for(int i = 0; i < NV; i ++) ///标记每个环,编号 { ret += in[i]; int v = i; while(vis[v] != i && id[v] == -1 && v != root) { vis[v] = i; v = pre[v]; } if(v != root && id[v] == -1) { for(int u = pre[v]; u != v; u = pre[u]) { id[u] = cntnode; } id[v] = cntnode ++; } } if(cntnode == 0) break;//无环 for(int i = 0; i < NV; i ++) if(id[i] == -1) id[i] = cntnode ++; ///步骤3:缩点,重新标记 for(int i = 0; i < NE; i ++) { int u=e[i].u; int v = e[i].v; e[i].u = id[u]; e[i].v = id[v]; if(e[i].u != e[i].v) e[i].w -= in[v]; } NV = cntnode; root = id[root]; } return ret;///最小树形图的长度 } int main() { int n,m,sum; int u,v,w; while(scanf("%d %d",&n,&m)!=EOF) { cnt=0;sum=0; for(int i=0; i<m; i++) { scanf("%d %d %d",&u,&v,&w); addedge(u+1,v+1,w); sum+=w; } sum++; for(int i=1; i<=n; i++) addedge(0,i,sum); int ans=Directed_MST(0,n+1,cnt); if(ans==-1||ans>=2*sum) printf("impossible\n\n"); else printf("%d %d\n\n",ans-sum,minroot-m); } return 0; }