hdu 2121 Ice_cream’s world II(不定根的最小树形图)

17 篇文章 0 订阅

Ice_cream’s world II

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


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
题意:给出一个有向图,求这个图的最小树形图,它的根是不定的,要求出最小树形图的权值和根的标号,若根的标号不唯一,输出最小的标号。
思路:这题要求不定根的最小树形图。做法是:虚拟出一个虚根,将虚根连到原图中的各点,每条虚边的权值相等且比原图中所有边的权值的总和还要大(将它设为 原图中所有边的权值总和+1即可),最后将最小树形图的权值减去虚边的权值即为最终结果。在找最小入边时,先选择原图上的边,再选择虚边,若能加入一条虚边进最小树形图,那么与虚边相连的必定就是最后最小树形图的根。而且只能加入一条虚边,如果加入了两条虚边,则说明原图中孤立的点不止一个,这种情况不存在最小树形图。
AC代码:
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <iostream>
#define max2(a,b) ((a) > (b) ? (a) : (b))
#define min2(a,b) ((a) < (b) ? (a) : (b))

using namespace std;
const double INF=10000000;
const int N=1005;

struct node
{
    int u,v;
    int w;
}edge[12000];
int in[N];
int pre[N],hash[N],vis[N];
int n,cntedge,minroot;

void add(int u,int v,int w)
{
    edge[cntedge].u=u;
    edge[cntedge].v=v;
    edge[cntedge].w=w;
    cntedge++;
}
int Directed_MST(int root)
{
    int sum=0;
    while(1)
    {
        for(int i=0; i<=n; i++)
            in[i]=INF;
        for(int i=0; i<cntedge; i++) //找最小入边
        {
            int u=edge[i].u;
            int v=edge[i].v;
            if(edge[i].w<in[v]&&u!=v)  //重构的新图可能存在自环
            {
                if(u==root) minroot=i;    //若虚边比原来的入边小,则对应的点在最小生成图中与虚根相连
                pre[v]=u;
                in[v]=edge[i].w;
            }
        }
        for(int i=0; i<=n; i++) //判断是否存在最小树形图
        {
            if(i==root) continue;
            if(in[i]==INF) return -1;
        }
        int cntnode=0;
        memset(hash,-1,sizeof(hash));
        memset(vis,-1,sizeof(vis));
        in[root]=0;
        for(int i=0; i<=n; i++)  //检查是否存在环
        {
            sum+=in[i];
            int v=i;
            while(vis[v]!=i&&hash[v]==-1&&v!=root)
            {
                vis[v]=i;
                v=pre[v];
            }
            if(v!=root&&hash[v]==-1)      //若存在环,将环缩点
            {
                for(int u=pre[v]; u!=v; u=pre[u])
                    hash[u]=cntnode;
                hash[v]=cntnode++;
            }
        }
        if(cntnode==0) break;   //若不存在环,算法终止
        for(int i=0; i<=n; i++) //构新图
            if(hash[i]==-1)
                hash[i]=cntnode++;
        for(int i=0; i<cntedge; i++)
        {
            int v=edge[i].v;
            edge[i].u=hash[edge[i].u];
            edge[i].v=hash[edge[i].v];
            if(edge[i].u!=edge[i].v)
                edge[i].w-=in[v];
        }
        n=cntnode-1;
        root=hash[root];
    }
    return sum;
}
int main()
{
    int a,b,m,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        cntedge=0;
        int sum=0;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(a==b) continue;      //不能存在自环
            sum+=c;
            add(a,b,c);
        }
        m=cntedge;
        sum++;               //设虚边的权值为所有边权值和+1
        for(int i=0;i<n;i++)
        add(n,i,sum);        //将虚边与各个点相连
        int ans=Directed_MST(n);
        if(ans==-1||ans>=2*sum)    //ans>=2*sum表示加入了两条虚边,表明原图不连通,不存在生成树
        printf("impossible\n\n");
        else
        printf("%d %d\n\n",ans-sum,minroot-m);  //虚边的标号-原图边的总数即为根的标号。
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值