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);
        }
    }
  • 相关阅读:
    第30节:Java基础-内部类
    第30节:Java基础-内部类
    gridview无数据源实现更新数据库(即断开更新数据库)
    net8:文本文件的创建及其读写
    智勇之家网页颜色代码自动生成
    对象数据源objectdatasource的使用,类的编写实现查询增删改的方法
    清清月儿.net学习技术资料网站
    可移植的数据库
    net3:文件上传与图片显示以及HiddenField隐藏字段值的使用
    C#.net磁盘管理以及文件操作
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10372510.html
Copyright © 2011-2022 走看看