hdu 4067 Random Maze

来源:互联网 发布:阿历克斯梅森 知乎 编辑:程序博客网 时间:2024/06/05 07:04
/*

  // 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4067

    解题报告人:SpringWater
    题意描述:

200个点的图,告诉你一些单向边,每条边两个值a和b,表示选与不选的花费。

现在让起点的出度-入度=1,终点的入度-出度=1,其他点的入度=出度。

问是否可能,可能的话最小花费是多少。


具体算法如下:

Sum表示初始图的代价,开始时为0.

对于b>=a的边,那么默认这条边连接,边权为b-a(删除的费用),当前费用sum+=a,由于默认这条边连接,那么出度[from]++, 入度[to]++

对于b<a的边,那么默认这条边没有连接,加入to到from的反向边,在流中就表示用这条反向边维护节点度的平衡,边权为a-b(添加的费用),当前费用sum+=b,由于默认不加这条边,所以出度入度就不用更新。

超级源点S,汇点T

出度>入度的,S连接之,容量为差值

入度>出度的,连接T,容量为差值

(注意图中起点和终点的+1-1的要求)

这样,如果满流,每个节点就满足了度的要求。

Sum+费用就是答案。

 


  回头感悟:这题时我acm以来用时最多的一个题目,感觉有些悲剧,不过终于把他拿下来了,还是蛮兴奋的,因为在为了ac它的过程中,

  我同时学到了其他很多有关网络流的算法知识,如最小最大网络流求法,有上下界的最小最大网络,最小费用最大流;特别是让人拍案叫绝的

  加边法,和让我感叹不已的spfa算法;还有就是发现了Djstla的一个经典局限性,(即对于存在负权边不适用)!不过此题最经典的地方还是
 
  在数学建模上:将此问题转化为一个求最大流;是我觉得最巧妙的地方,原因是:即使求最小费用最大流虽然已经涉及到了很多经典算法,但
 
  是这些方法都已经是 模板了,不足为绝唱(不过就我亲自实现了一番的感觉而言,还是感觉它足以让人累疼,加肉疼!)


 
*/
#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define MAXEDGE 54000#define BREAK 1000000000struct Edge{    int flow;    int cost;    int point;    int forhead;}edge[MAXEDGE];int path[210];int que[220];int code[210],num;int weight[210];int visit[210];int in[210],out[210];int AddEdge(int man,int son,int cost,int flow){    edge[num].cost=cost,edge[num].flow=flow,edge[num].forhead=code[man],edge[num].point=son;    code[man]=num;    ++num;    edge[num].cost=-cost,edge[num].flow=0,edge[num].forhead=code[son],edge[num].point=man;    code[son]=num;    ++num;    return 0;}int FindAMinCostWay(int n,int sta,int end){    int i,j;    int l,r,temp;    for(i=1;i<=n;i++)        weight[i]=BREAK;    l=0,r=1;    que[0]=sta;    weight[sta]=0;    for(i=que[l];l!=r;i=que[l])    {        for(j=code[i],visit[i]=0;j!=-1;j=edge[j].forhead)        {            if(edge[j].flow&&(weight[edge[j].point]>(temp=weight[i]+edge[j].cost)))            {                weight[edge[j].point]=temp;                path[edge[j].point]=j^1;                if(visit[edge[j].point])continue;                que[r]=edge[j].point;                visit[edge[j].point]=1;                ++r;                if(r>=200)                    r=0;            }        }        ++l;        if(l>=200)            l=0;    }    if(weight[end]==BREAK)        return 0;    else        return 1;///这里把我坑害惨了;因为weight[end]==0时也可能满足}                ///,使得返回值即使是0不能排除存在一条最短路径int main(){    int T,cas;    int n,m,s,t;    int u,v,a,b;    int end,sta;    int MaxFlow;    int i;    int ans,flow,cost;    freopen("input.txt","r",stdin);    scanf("%d",&T);    for(cas=1;cas<=T;cas++)    {        scanf("%d%d%d%d",&n,&m,&s,&t);        memset(in,0,sizeof(in));        memset(out,0,sizeof(out));        num=0;        memset(code,-1,sizeof(code));        ans=0;        for(i=1;i<=m;i++)        {            scanf("%d%d%d%d",&u,&v,&a,&b);            ans+=(a<b)?a:b;            if(a<b)                AddEdge(v,u,b-a,1),out[u]++,in[v]++;            else                AddEdge(u,v,a-b,1);        }        out[t]++,in[s]++;        end=n+2,sta=n+1;        MaxFlow=0;        for(i=1;i<=n;i++)        {            if(out[i]>in[i])                AddEdge(i,end,0,out[i]-in[i]),MaxFlow+=out[i]-in[i];            if(out[i]<in[i])                AddEdge(sta,i,0,in[i]-out[i]);        }        flow=0;        while(1)        {            cost=FindAMinCostWay(n+2,sta,end);            if(cost)            {                ++flow;                ans+=weight[end];                path[sta]=-1;            for(i=end;path[i]!=-1;i=edge[path[i]].point)                    edge[path[i]].flow+=1,edge[path[i]^1].flow-=1;            }            else                break;        }        printf("Case %d: ",cas);        if(MaxFlow==flow)            printf("%d\n",ans);        else            printf("impossible\n");    }    return 0;}




原创粉丝点击