hdu2121 - Ice_cream’s world II(朱刘算法,不固定根)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2121
题目意思大概是要你在一些城市中选一个做首都 , 要求首都都能到其他城市 , 道路花费要最少 , 且道路都是单向的 , 这个时候就要用到最小树形图算法了 , 而且是不固定根.
不定根就是加一个虚根(原本不存在的点) , 可以让这个虚根到每个点的距离大于原本所有点连接的道路花费之和sum , 然后计算出的结果减去sum,如果比sum还大就可以认为通过这个虚拟节点我们连过原图中两个点,即原图是不连通的,我们就可以认为不存在最小树形图。那么真正的根呢 , 在找最小入弧时,如果这条弧的起点是虚拟根,那么这条弧的终点就是要求的根,因为如果有多解的话,必然存在一个环,环上的顶点都可以做根,但是我们根据最小入边的性质,可知,如果没缩点,必然找不到那个根,因为虚拟根连的边都非常大。但是缩点后,找到的必然是最小的那个序号的根。(画图理解)
下面是代码:
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const int MAXN = 1e3 + 10; typedef long long LL; int id[MAXN] , vis[MAXN] , pre[MAXN] , pos; LL INF = 1e17 , d[MAXN]; struct node { int u , v , cost; }edge[MAXN * MAXN]; LL zhuliu(int root , int V , int E) { LL res = 0; while(true) { for(int i = 0 ; i < V ; i++) { d[i] = INF; } for(int i = 0 ; i < E ; i++) { int u = edge[i].u , v = edge[i].v; if(u != v && d[v] > edge[i].cost) { d[v] = edge[i].cost; pre[v] = u; if(u == root) { pos = i; //记录位置 除了这里不一样 其他地方都是朱刘算法的模板 } } } for(int i = 0 ; i < V ; i++) { if(d[i] == INF && i != root) { return -1; } } int cont = 0; memset(id , -1 , sizeof(id)); memset(vis , -1 , sizeof(vis)); d[root] = 0; for(int i = 0 ; i < V ; i++) { int v = i; res += d[i]; while(id[v] == -1 && vis[v] != i && v != root) { vis[v] = i; v = pre[v]; } if(id[v] == -1 && v != root) { for(int u = pre[v] ; u != v ; u = pre[u]) { id[u] = cont; } id[v] = cont++; } } if(!cont) { break; } for(int i = 0 ; i < V ; i++) { if(id[i] == -1) { id[i] = cont++; } } for(int i = 0 ; i < E ; i++) { int u = edge[i].u , v = edge[i].v; edge[i].u = id[u]; edge[i].v = id[v]; if(id[u] != id[v]) { edge[i].cost -= d[v]; } } V = cont; root = id[root]; } return res; } int main() { int n , m; while(~scanf("%d %d" , &n , &m)) { LL sum = 0; for(int i = 0 ; i < m ; i++) { scanf("%d %d %d" , &edge[i].u , &edge[i].v , &edge[i].cost); edge[i].u++ , edge[i].v++; sum += edge[i].cost; } sum++; //边权大于总权值 for(int i = m ; i < n + m ; i++) { edge[i].u = 0; //0为虚拟节点 edge[i].v = i - m + 1; edge[i].cost = sum; } LL res = zhuliu(0 , n + 1 , n + m); //n + 1 个点 n + m 条边 if(res == -1 || res - sum >= sum) { //要是res - sum < sum 的话就说明 0的出度为1 说明原图是连通图 printf("impossible\n\n"); } else { printf("%lld %d\n\n" , res - sum , pos - m); } } }
不好意思.写的很搓.