
hdu 2121 不定根最小树形图模板题 朱刘算法 + 虚根
发布日期:2021-05-06 15:24:57
浏览次数:27
分类:精选文章
本文共 3701 字,大约阅读时间需要 12 分钟。
题意:求一个n个点、m条有向边的有向图的不定根的最小树形图,并确定根结点。
题解:朱刘算法 虚根
1.最小树形图要用朱刘算法模板。
2.确定根需要找一个虚根,编号记为n,原先的n个点为0~n-1,新加n条边,这n条边均由第n点指向第0~n-1个点,且这n条边的权值为sum+1,sum是原m条边的权值和。然后套用朱刘模板,若求出的最小树形图和>=2*sum,则用了两条新加边,因为新加边长于所有原边,所以原图不能形成最小树形图。因新加了n条边,故新图一定有最小树形图,zhuliu函数不会返回-1。
3.根节点需要在新加边中确定,因为缩点会导致原先点的下标变化,不能在zhuliu函数后由pre[i]==n确定,此时的i不再是原先的点下标了,缩点不会导致新加的n条边确定,新加的n条边的终点为原先的0~n-1下标,故在运行zhuliu函数时由新加边确定根节点。
4.上述若不易理解,可参考代码注释。
/* 最小树形图 朱刘算法模板 时间复杂度O(nm) 数据为int型 */ #include#include #include #include #define MAXN 2001 #define MAXM 20000+10 #define inf 1e17 using namespace std; struct Edge { int from, to ; long long cost; }; Edge edge[MAXM]; int pre[MAXN];//存储父节点 int vis[MAXN];//标记作用 int id[MAXN];//id[i]记录节点i所在环的编号 long long in[MAXN];//in[i]记录i入边中最小的权值 int capa ;long long zhuliu(int root, int n, int m, Edge *edge)//root根 n点数 m边数 { long long res = 0 ; int u , v ; while(1) { for(int i = 0 ; i < n ; i++) in[i] = inf ;//初始化 for(int i = 0 ; i < m ; i++) { Edge E = edge[i]; if(E.from != E.to && E.cost < in[E.to]) { pre[E.to] = E.from;//记录前驱 in[E.to] = E.cost;//更新 if(E.from == root) //因为有缩点,每个点的下标发生了变化,但新加的边的指向未变, //所以可以通过这个边找到原来点的下标 capa = i ; } } for(int i = 0 ; i < n ; i++) if(i != root && in[i] == inf) return -1;//有其他孤立点 则不存在最小树形图 //找有向环 int tn = 0 ;//记录当前查找中 环的总数 memset(id, -1, sizeof(id)); memset(vis, -1, sizeof(vis)); in[root] = 0;//根 for(int i = 0 ; i < n; i++) { res += in[i];//累加 v = i; //找图中的有向环 三种情况会终止while循环 //1,直到出现带有同样标记的点说明成环 //2,节点已经属于其他环 //3,遍历到根 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] = tn;//记录节点所属的 环编号 id[v] = tn ++ ;//记录节点所属的 环编号 环编号累加 } } if(tn == 0) break;//不存在有向环 //可能存在独立点 for(int i = 0 ; i < n ; i++) if(id[i] == -1) id[i] = tn ++ ;//环数累加 //对有向环缩点 和SCC缩点很像吧 for(int i = 0; i < m ; i++) { v = edge[i].to; edge[i].from = id[edge[i].from]; edge[i].to = id[edge[i].to]; // 有向边 //两点不在同一个环 u到v的距离为 边权cost - in[v] if(edge[i].from != edge[i].to) edge[i].cost -= in[v] ;//更新边权值 继续下一条边的判定 } n = tn;//以环总数为下次操作的点数 继续执行上述操作 直到没有环 root = id[root]; } return res; } int main() { int n , m ;//N个点 M条有向边 int i , j ; int t ; long long ans ; while(scanf("%d%d", &n, &m) != EOF) { long long sum = 0 ; for(i = 0 ; i < m ; i ++) { scanf("%d%d%lld" , &edge[i].from , &edge[i].to , &edge[i].cost) ; sum += edge[i].cost ; } sum ++ ; for(i = m ; i < m + n ; i ++) { edge[i].from = n ; edge[i].to = i - m ; edge[i].cost = sum ; } ans = zhuliu(n , n + 1 , m + n , edge); if(ans >= 2 * sum) printf("impossible\n\n") ; //不存在 else { ans -= sum ; //虚边删除 capa -= m ; //首都的下标需减去原边数 printf("%lld %d\n\n" , ans , capa) ; } } return 0 ; }
发表评论
最新留言
表示我来过!
[***.240.166.169]2025年04月15日 05时38分12秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Redis 集合统计(HyperLogLog)
2021-05-09
Kafka 博文索引
2021-05-09
Dynamics CRM实体系列之字段
2021-05-09
RE套路 - 关于pyinstaller打包文件的复原
2021-05-09
【wp】HWS计划2021硬件安全冬令营线上选拔赛
2021-05-09
Ef+T4模板实现代码快速生成器
2021-05-09
dll详解
2021-05-09
c++ static笔记
2021-05-09
C++中头文件相互包含与前置声明
2021-05-09