HDU ACM 2121 Ice_cream’s world II (无根最小树形图)

时间:2021-04-28 00:20:18

【解题思路】这题先看了NotOnlySuccess的解题思路,即设置虚根再处理的做法;弄了一个上午,再次有种赶脚的感觉~~如果需要找出为什么需要去比所有权值之和更大的数为新增的虚边的话,一开始我理解仅是找比最大边权值要大的数就够了,因为这样就已经满足了不会提前找到虚边的情况,但如果是这样的话那就很难判断这样一种情况:生成的最小树形图(含虚根)有两条边是从虚根出来的。

 #include<cstdio>
#include<cstring>
#define SIZE 1002
#define MAXN 20004 using namespace std; const int INF = <<;
int nv, ne, aimroot;
int vis[SIZE], pre[SIZE], num[SIZE];
int inLength[SIZE]; struct Edge{
int u, v, w;
}edge[MAXN]; void addedge(int u, int v, int w)
{//添加虚根与原图中其他点相连的边
edge[ne].u = u;
edge[ne].v = v;
edge[ne].w = w;
return;
} bool Traverse(int& res, int root)
{
nv++;
while(true)
{
for(int i=; i<nv; ++i) inLength[i] = INF;
for(int i=; i<ne; ++i)
{
int& u = edge[i].u;
int& v = edge[i].v;
if(edge[i].w < inLength[v] && u != v)
{
//不管结点的Id号怎样变化,固定的边所在的位置是不会变的,根据边在edge数组中存储的位置与起初边的Id号的对应关系
//就能理解下面为什么要这样输出
if(u == root) aimroot = i;
inLength[v] = edge[i].w;
pre[v] = u;
}
}
for(int i=; i<nv; ++i)
if(i != root && inLength[i] == INF) return false;
int newnum = ;
inLength[root] = ;
memset(vis, -, sizeof(vis));
memset(num, -, sizeof(num));
for(int i=; i<nv; ++i)
{
res += inLength[i];
int v = i;
while(vis[v] != i && num[v] == - && v != root)
{
vis[v] = i;
v = pre[v];
}
if(vis[v] == i)
{
for(int u=pre[v]; u != v; u = pre[u])
num[u] = newnum;
num[v] = newnum++;
}
}
if(newnum == ) return true;
for(int i=; i<nv; ++i)
if(num[i] == -) num[i] = newnum++;
for(int i=; i<ne; ++i)
{
int u = edge[i].u;
int v = edge[i].v;
edge[i].u = num[u];
edge[i].v = num[v];
if(edge[i].u != edge[i].v)
edge[i].w -= inLength[v];
}
nv = newnum;
root = num[root];
} } int main()
{
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
#endif
while(scanf("%d%d", &nv, &ne) != EOF)
{
int u, v, w, sum = ;
for(int i=; i<ne; ++i)
{
scanf("%d%d%d", &u, &v, &w);
edge[i].u = u;
edge[i].v = v;
edge[i].w = u == v ? INF+ : w;
sum += w;
}
sum++;
int temp = ne;
for(int i=; i<nv; ++i, ++ne)
addedge(nv, i, sum);
int res = ;
//res < 2*sum 设立这个条件是因为考虑到这种情况,虚根引出两条出边,这是不允许的,必须承认这是虚根,你得在原图中找到一个根
//这个条件成立了,说明有两条以上的虚边引出
if(Traverse(res, nv) && res < *sum)
//结合上面addedge函数中的说明,得到的aimroot存储的是你初始新存储虚边是在edge的下标,此下标大于或等于原图边的数目(边是从
//从零下标开始存储
printf("%d %d\n\n", res-sum, aimroot-temp);
else
printf("impossible\n\n");
}
return ;
}