hdoj 2121 Ice_cream’s world II 【虚拟源点求无根树的最小树形图】



Ice_cream’s world II

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3574    Accepted Submission(s): 859


Problem Description
After awarded lands to ACMers, the queen want to choose a city be her capital. This is an important event in ice_cream world, and it also a very difficult problem, because the world have N cities and M roads, every road was directed. Wiskey is a chief engineer in ice_cream world. The queen asked Wiskey must find a suitable location to establish the capital, beautify the roads which let capital can visit each city and the project’s cost as less as better. If Wiskey can’t fulfill the queen’s require, he will be punishing.
 

Input
Every case have two integers N and M (N<=1000, M<=10000), the cities numbered 0…N-1, following M lines, each line contain three integers S, T and C, meaning from S to T have a road will cost C.
 

Output
If no location satisfy the queen’s require, you must be output “impossible”, otherwise, print the minimum cost in this project and suitable city’s number. May be exist many suitable cities, choose the minimum number city. After every case print one blank.
 

Sample Input
       
       
       
       
3 1 0 1 1 4 4 0 1 10 0 2 10 1 3 20 2 3 30
 

Sample Output
       
       
       
       
impossible 40 0
 



题意:给你N个点(编号从0到N-1)和M条有向边以及它的权值,可以任意选根节点。让你求出以该节点为根的最小树形图,并输出根节点编号。 若有多个最小树形图,输出根节点编号较小的。



思路:虚拟根节点N,求出图中所有边权之和sum,然后sum+=1。虚根到所有点建边,边权为sum。

遵循——1,按照点序号从小到大  2,新边的编号依次 从M到N+M-1。

以虚根为最小树形图的根,跑一次朱刘算法。若不存在最小树形图或者求出结果ans >= 2 * sum 则说明原图不存在最小树形图。反之存在,答案为ans - sum。我们可以用一个变量start记录每次选择入边集时,起点为虚根的边的编号,最后start-M就是最小树形图的根节点编号。


为什么ans >= 2 * sum就是不存在最小树形图?

先声明——sum - 1是原图中所有边的边权之和,并且虚根与每个点的边权为sum。

当我们以虚根为根节点时,若原图存在最小树形图

1,那么虚根只能与原图中一个点建边,这样的话结果就有了一部分 ans1 = sum。

 

2,最坏情况下——原图中最小树形图值ans2 = sum-1 即原图中所有边都用上了。

 

得到结果ans = ans1 + ans2 = sum + sum - 1,这是最大化的值。所以当ans >= 2 * sum时,当然可以说明不存在最小树形图。

 

 

AC代码:

 

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 1010
#define MAXM 20000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cost;
};
Edge edge[MAXM];
int pre[MAXN];
int vis[MAXN];
int id[MAXN];//id[i]记录节点i所在环的编号
int in[MAXN];
int N, M;
int start;//记录起点
int sum;//记录图中所有边权值和
void getMap()
{
    int a, b, c;
    sum = 0;
    for(int i = 0; i < M; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        sum += c;
        edge[i].from = a;
        edge[i].to = b;
        edge[i].cost = a==b ? INF : c;
    }
    sum++;
    for(int i = M; i < N+M; i++)
    {
        edge[i].from = N;//虚根
        edge[i].to = i - M;//按点序号从小到大
        edge[i].cost = sum;
    }
}
int zhuliu(int root, int n, int m, Edge *edge)
{
    int res = 0, 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;//记录前驱
                if(E.from == root)
                    start = i;//记录边号
                in[E.to] = E.cost;
            }
        }
        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(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++;
        //对有向环缩点
        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];
            if(edge[i].from != edge[i].to)
                edge[i].cost -= in[v];
        }
        n = tn;
        root = id[root];
    }
    return res;
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        getMap();
        int ans = zhuliu(N, N+1, N+M, edge);
        if(ans == -1 || ans >= 2 * sum)//不存在最小树形图
            printf("impossible\n");
        else
            printf("%d %d\n", ans - sum, start - M);
        printf("\n");
    }
    return 0;
}


 

你可能感兴趣的:(hdoj 2121 Ice_cream’s world II 【虚拟源点求无根树的最小树形图】)