链接:http://acm.hdu.edu.cn/showproblem.php?
pid=2121
题意:n个顶点,m条边,求从某一点起建立有向图最小生成树而且花费最小。输出最小花费和根节点下标。
思路:这道题根是不确定的,我们能够先如果一个根。从这个根出发到不论什么一点的距离(sum)都比原图总权值还大。这样保证了虚拟的边不会是最小入边,也为之后推断是否生成了最小树形图提供方便,从这个点開始建立最小树形图,最后生成出一个结果。非常显然虚拟的根仅仅有一条出边。而且出边连接的点就是真实的根。
最后得到的最小树形图权值,减去虚拟边的权值sum。假设结果大于等于sum,说明虚拟顶点的出边不止一条,也即假设不靠这个虚拟顶点原图无法构成最小树形图,这是最小树形图的还有一个推断条件。
至于真实的根,找到某点的最小入边,假设出边的点是虚拟根,则这个点就是真实根,可是我们不能直接记录这个顶点。由于这个顶点可能是缩点后的点,所以我们记录边的编号,由于虚拟边的编号是从m開始添加n条边的。最后用记录的边的编号减去m就是真实根的下标。
/* 最小树形图图模版-朱刘算法 模版说明:点标号必须0-(N-1) 必须去除到自身的点(到自身的边的边权赋无限大) */ #include<cstring> #include<string> #include<fstream> #include<iostream> #include<iomanip> #include<cstdio> #include<cctype> #include<algorithm> #include<queue> #include<map> #include<set> #include<vector> #include<stack> #include<ctime> #include<cstdlib> #include<functional> #include<cmath> using namespace std; #define PI acos(-1.0) #define MAXN 50100 #define eps 1e-7 #define INF 0x7FFFFFFF #define seed 131 #define mod 1000000007 #define ll long long #define ull unsigned ll #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct node{ int u,v; int dis; }edge[20100]; int pre[1010],ID[1010],vis[1010]; int n,m; int In[1010]; int ansp; 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; for(int i=0;i<NE;i++){ int u = edge[i].u; int v = edge[i].v; if(edge[i].dis < In[v] && u != v) { pre[v] = u; In[v] = edge[i].dis; if(u==root) ansp = i; //实际上应该等于v。可是v有可能是缩点,所以等于i。之后再减去m就是顶点编号 } } for(int i=0;i<NV;i++) { if(i == root) continue; if(In[i] == INF) return -1;//除了根以外有点没有入边,则根无法到达它 } //2.找环 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 v = edge[i].v; edge[i].u = ID[edge[i].u]; edge[i].v = ID[edge[i].v]; if(edge[i].u != edge[i].v) { edge[i].dis -= In[v]; } } NV = cntnode; root = ID[root]; } return ret; } int main(){ int i,j,a,b,c; double temp; while(scanf("%d%d",&n,&m)!=EOF){ int x = m; int sum = 0; for(i=0;i<m;i++){ scanf("%d%d%d",&a,&b,&c); edge[i].u = a + 1; edge[i].v = b + 1; edge[i].dis = c; sum += c; } sum++; for(i=m;i<m+n;i++){ edge[i].u = 0; edge[i].v = i - m + 1; edge[i].dis = sum; } int ans = Directed_MST(0,n+1,m+n); if(ans==-1||ans-sum>=sum) puts("impossible"); else printf("%d %d ",ans-sum,ansp-x); puts(""); } return 0; }