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

来源:互联网 发布:罗德与施瓦茨 知乎 编辑:程序博客网 时间:2024/06/06 16:12

Ice_cream’s world II

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


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 10 1 14 40 1 100 2 101 3 202 3 30
 

Sample Output
impossible40 0
 

Author
Wiskey
 

Source
HDU 2007-10 Programming Contest_WarmUp 
 

Recommend
威士忌

题目大意:

    给出一个有向图,让你确定一个根结点,使得最小树形图的花费最小。


解题思路:

    朱刘算法是解决确定根最小树形图的算法,对于不定根的情况,枚举根节点是可以达到目的的,但时间复杂度过高。

    通过增加个超级源点可以跑一次朱刘算法就确定根节点和花费。从超级源点出发向每个点连一条花费为所有边费用之和+1的边(为了使额外边不影响原图中边的决策)。然后把超级源点作为起点跑朱刘算法,得到的花费-额外边的花费就是不定根最小树形图的花费。当算法中断或得到的花费>=2*额外边时说明无法构成树形图。加入新树形图的额外边在缩点前指向的点就是所求源点。添加额外边的时候,按照终点从小到大添加,就可在所求源点有多组解的时候找到最小的。


AC代码:

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <ctime>#include <vector>#include <queue>#include <stack>#include <deque>#include <string>#include <map>using namespace std;#define INF 0x3f3f3f3f3f3f3f3f#define LL long long#define fi first#define se second#define mem(a,b) memset((a),(b),sizeof(a))const LL MAXV=1000+3;const LL MAXE=11000+3;struct Edge{    LL u,v,cost;}edge[MAXE];LL pre[MAXV],id[MAXV],vis[MAXV],in[MAXV];LL V,M,E,sum,ans_index;void init(){    E=M;    sum=0;}LL zhuliu(LL root){    LL res=0;    while(true)    {        for(LL i=0;i<V;++i)            in[i]=INF;        for(LL i=0;i<E;++i)            if(edge[i].u!=edge[i].v&&edge[i].cost<in[edge[i].v])            {                pre[edge[i].v]=edge[i].u;                in[edge[i].v]=edge[i].cost;                if(edge[i].u==root)//找到指向原图源点的边                    ans_index=i;            }        for(LL i=0;i<V;++i)            if(i!=root&&in[i]==INF)                return -1;        LL tn=0;        mem(id,-1);        mem(vis,-1);        in[root]=0;        for(LL i=0;i<V;++i)        {            res+=in[i];            LL v=i;            while(vis[v]!=i&&id[v]==-1&&v!=root)            {                vis[v]=i;                v=pre[v];            }            if(v!=root&&id[v]==-1)            {                for(LL u=pre[v];u!=v;u=pre[u])                    id[u]=tn;                id[v]=tn++;            }        }        if(tn==0)            break;        for(LL i=0;i<V;++i)            if(id[i]==-1)                id[i]=tn++;        for(LL i=0;i<E;++i)        {            LL v=edge[i].v;            edge[i].u=id[edge[i].u];            edge[i].v=id[edge[i].v];            if(edge[i].u!=edge[i].v)//由于要利用边的下标找原始源点,这里就不能把无用边放到后面                edge[i].cost-=in[v];//            else swap(edge[i],edge[--E]);        }        V=tn;        root=id[root];    }    return res;}int main(){    while(~scanf("%lld%lld",&V,&M))    {        // 0 ~ V-1 点        // V       超级源点        init();        for(LL i=0;i<M;++i)        {            scanf("%lld%lld%lld",&edge[i].u,&edge[i].v,&edge[i].cost);            sum+=edge[i].cost;        }        ++sum;        for(int i=0;i<V;++i)//从小到大加入额外边        {            edge[E].u=V;            edge[E].v=i;            edge[E++].cost=sum;        }        ++V;        LL ans=zhuliu(V-1);        if(ans==-1||ans-sum>=sum)//无解            puts("impossible");        else printf("%lld %lld\n",ans-sum,ans_index-M);//直接通过找到的边的下标运算出这条边的终点        putchar('\n');    }        return 0;}

阅读全文
0 0
原创粉丝点击