题目大意:给你N个点,M条有向边,问以哪个点为根结点时,能使最小生成树总权值达到最小,输出总权值和根。
如果构不成最小生成树,另外输出
解题思路:这题很巧妙,暴力枚举的话,肯定TLE,所以,这题就需要点技巧了
可以设一个虚根,虚根连接每一个点,权值为所有边的总权值+1。接着,以虚根为根,跑朱刘算法。
跑出结果后,要判断一下,如果最小生成树的总权值比2 * (所有边的总权值+1)还要大,表示虚根至少和两个点相连了,这样最小生成树就是棵假的最小生成树了,因为至少有两个点入度为0了
得到结果时要怎么找到根,可以用边来判断。我们先构造的是m条边,后面又构造了n条边(虚根到每个点),如果某个点的最小入权值为虚根到该点的权值了,那就证明该点就是最小根了。因为只有下标大于等于m的边才是虚根到点的边,所以只要纪录一下下标,最后输出时用下标-m就得到该点了
#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[N], vis[N], id[N], in[N];
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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU - 2121 Ice_cream’s world II(朱刘算法+虚根)
原文:http://blog.csdn.net/l123012013048/article/details/47711205