HDU - 2121 Ice_cream’s world II(朱刘算法+虚根)
2015-08-17 00:24
281 查看
题目大意:给你N个点,M条有向边,问以哪个点为根结点时,能使最小生成树总权值达到最小,输出总权值和根。
如果构不成最小生成树,另外输出
解题思路:这题很巧妙,暴力枚举的话,肯定TLE,所以,这题就需要点技巧了
可以设一个虚根,虚根连接每一个点,权值为所有边的总权值+1。接着,以虚根为根,跑朱刘算法。
跑出结果后,要判断一下,如果最小生成树的总权值比2 * (所有边的总权值+1)还要大,表示虚根至少和两个点相连了,这样最小生成树就是棵假的最小生成树了,因为至少有两个点入度为0了
得到结果时要怎么找到根,可以用边来判断。我们先构造的是m条边,后面又构造了n条边(虚根到每个点),如果某个点的最小入权值为虚根到该点的权值了,那就证明该点就是最小根了。因为只有下标大于等于m的边才是虚根到点的边,所以只要纪录一下下标,最后输出时用下标-m就得到该点了
如果构不成最小生成树,另外输出
解题思路:这题很巧妙,暴力枚举的话,肯定TLE,所以,这题就需要点技巧了
可以设一个虚根,虚根连接每一个点,权值为所有边的总权值+1。接着,以虚根为根,跑朱刘算法。
跑出结果后,要判断一下,如果最小生成树的总权值比2 * (所有边的总权值+1)还要大,表示虚根至少和两个点相连了,这样最小生成树就是棵假的最小生成树了,因为至少有两个点入度为0了
得到结果时要怎么找到根,可以用边来判断。我们先构造的是m条边,后面又构造了n条边(虚根到每个点),如果某个点的最小入权值为虚根到该点的权值了,那就证明该点就是最小根了。因为只有下标大于等于m的边才是虚根到点的边,所以只要纪录一下下标,最后输出时用下标-m就得到该点了
[code]#include <cstdio> #include <cstring> #define M 20010 #define N 1010 struct Edge{ int from, to, cost; }E[M]; int n, m, tot; int Sum; void AddEdge(int u, int v, int c) { E[tot].from = u; E[tot].to = v; E[tot++].cost = c; } void init() { tot = 0; Sum = 0; int u, v, c; for (int i = 0; i < m; i++) { scanf("%d%d%d", &u, &v, &c); AddEdge(u, v, c); Sum += c; } Sum++; for (int i = 0; i < n; i++) { AddEdge(n, i, Sum); } } #define INF 0x3f3f3f3f int minroot; int pre , vis , id , in ; int Directed_MST(int root) { int ans = 0, u, v, tmp; while (1) { memset(pre, -1, sizeof(pre)); for (int i = 0; i < n; i++) in[i] = INF; for (int i = 0; i < tot; i++) { u = E[i].from; v = E[i].to; if (u != v && E[i].cost < in[v]) { in[v] = E[i].cost; pre[v] = u; if (u == root) minroot = i; } } for (int i = 0; i < n; i++) { if (i == root) continue; if (in[i] == INF) return -1; } int cnt = 0; memset(vis, -1, sizeof(vis)); memset(id, -1, sizeof(id)); in[root] = 0; for (int i = 0; i < n; i++) { ans += in[i]; tmp = i; while (tmp != root && id[tmp] == -1 && vis[tmp] != i) { vis[tmp] = i; tmp = pre[tmp]; } if (tmp != root && id[tmp] == -1) { u = pre[tmp]; while (u != tmp) { id[u] = cnt; u = pre[u]; } id[tmp] = cnt++; } } if (cnt == 0) break; for (int i = 0; i < n; i++) if (id[i] == -1) id[i] = cnt++; for (int i = 0; i < tot; i++) { tmp = E[i].to; E[i].from = id[E[i].from]; E[i].to = id[E[i].to]; if (E[i].from != E[i].to) E[i].cost -= in[tmp]; } n = cnt; root = id[root]; } return ans; } void solve() { n++; int ans = Directed_MST(n - 1); if (ans == -1 || ans >= 2 * Sum) printf("impossible\n"); else printf("%d %d\n", ans - Sum, minroot - m); printf("\n"); } int main() { while (scanf("%d%d" ,&n, &m) != EOF) { init(); solve(); } return 0; }
相关文章推荐
- [LeetCode][JavaScript]Binary Tree Paths
- HDU 3530 RMQ+twopointer/单调队列
- 逆序对问题
- 老电脑如果从windows7升级到windows10不断重启进不了系统,还是想用windows10,怎么办?
- poj3984广搜c语言
- LeetCode Divide Two Integers
- LeetCode Divide Two Integers
- 文件对象-读操作
- Xcode7 采用空白模版运行错误
- cordova配置文件config.xm
- Linux 下 eclipse 创建快捷方式
- ORA-00257:archiver error问题处理方法
- Python的替换函数——strip(),replace()和re.sub()
- codeforces 566B Replicating Processes
- jQuery AJAX
- ORA-00257:archiver error问题处理方法
- linux添加静态路由
- Linux常用命令002之搜索命令locate、whereis、which、find、grep
- 逆向分析AHpack
- JavaScript的词法结构