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\n\n");
        else printf("%lld %lld\n\n",ans-sum,Rt-tmp-1ll);
    }
}

 

posted @ 2019-02-14 00:17  heyuhhh  阅读(210)  评论(0编辑  收藏  举报