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);
        }
    }
  • 相关阅读:
    使用Loadrunner监控Windows资源
    Tomcat使用线程池配置高并发连接
    性能测试中遇到的坑
    本地eclipse启动tomcat后无法访问
    Linux常用命令汇总
    Dubbo底层采用Socket进行通信详解
    今天遇到了一个Spring出现的一个未知错误,分享下
    maven pom.xml 详细
    Oracle 数据库中在使用中文模糊查询时输入中文查询不到结果的解决方法
    mybatis属性详解
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10372510.html
Copyright © 2011-2022 走看看