zoukankan      html  css  js  c++  java
  • HDU2121: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): 6849    Accepted Submission(s): 1818

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2121

    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

    题意:

    给出一个有向图,然后让你选一个点作为起点,满足从这个点可以到达其它点,并且总权和最小。

    题解:

    这就是一个不固定根的有向图最小生成树问题。思路还是挺有趣的。

    我们不可能对每个点都跑一遍朱刘算法,所以考虑加一个虚根,然后边权为INF,直接从这个虚根来跑就行了。最后答案就是跑出来的值减去INF。

    但是这里要注意的是一些不合法的情况,就是最后的答案大于2*INF时,因为这时,说明至少有两个点入度为0,那么说明不可能存在一颗有向图的生成树。

    至于这个INF怎么取,只要满足大于等于边权和就行了(我是这么想的),不知道为什么不能等于,这个问题我纠结了很久,就在刚才知道为什么了。。

    可能存在只有一条边的情况,这时如果连边权和,这时算法可能会出错。

    最后输出起点,这里比较巧妙,是根据边来的,因为我们会改变点(算法缩点),但边并没有改变,具体见代码:

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #define INF 1e17
    using namespace std;
    typedef long long ll;
    int n,m,t;
    const int N = 1010,M = 10005;
    struct Edge{
        int u,v,w;
    }e[M<<1];
    int pre[N]; //记录前驱
    int Rt;
    ll id[N],vis[N],in[N];
    ll dirMst(int root){
        ll ans=0;
        while(1){
            for(int i=0;i<=n;i++) in[i]=INF;
            memset(id,-1,sizeof(id));
            memset(vis,-1,sizeof(vis));
            for(int i=1;i<=m;i++){
                int u=e[i].u,v=e[i].v,w=e[i].w;
                if(w<in[v] && v!=u){
                    pre[v]=u;
                    in[v]=w;
                    if(u==root) Rt=i;
                }
            }           //求最小入边集
            in[root]=0;
            pre[root]=root;
            for(int i=0;i<n;i++){
                if(in[i]==INF) return -1;
                ans+=in[i];
            }
            int idx = 0; //新标号
            for(int i=0;i<n;i++){
                if(vis[i] == -1 ){
                    int u = i;
                    while(vis[u] == -1){
                        vis[u] = i;
                        u = pre[u];
                    }
                    if(vis[u]!=i || u==root) continue;     //判断是否形成环
                    for(int v=pre[u];v!=u;v=pre[v] )
                        id[v]=idx;
                    id[u] = idx++;
                }
            }
            if(idx==0) break;
            for(int i=0;i<n;i++){
                if(id[i]==-1) id[i]=idx++;
            }
            for(int i=1;i<=m;i++){
                e[i].w-=in[e[i].v];
                e[i].u=id[e[i].u];
                e[i].v=id[e[i].v];
            }
            n = idx;
            root = id[root];//给根新的标号
        }
        return ans;
    }
    
    int main(){
        while(scanf("%d%d",&n,&m)!=EOF){
            ll sum = 0;
            for(int i=1;i<=m;i++){
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                e[i]=Edge{u,v,w};
                sum+=w;
            }
            sum++;
            for(int i=0;i<n;i++){
                e[i+m+1]=Edge{n,i,sum};
            }
            ll tmp=m;
            m+=n;n++;
            ll ans = dirMst(n-1);
            if(ans>2*sum) printf("impossible
    
    ");
            else printf("%lld %lld
    
    ",ans-sum,Rt-tmp-1ll);
        }
    }
  • 相关阅读:
    swoole学习(四):websocket
    LeetCode--SQL 查询:体育馆的人流量
    LeetCode--SQL 查询:有趣的电影
    centos7下mysql5.7忘记密码
    LeetCode--SQL 查询:删除重复的电子邮箱。
    swoole学习(三):HTTP服务
    swoole学习(二):TCP/UDP服务器-客户端
    swoole学习(一):初识swoole
    LeetCode--SQL 查询:查找部门工资前三高的职员
    报文、报文段、分组、包、数据报、帧、数据流的概念区别
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10372510.html
Copyright © 2011-2022 走看看