hdu 4067 Random Maze

/*

  // 题目链接: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 1000000000
struct 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;
}




你可能感兴趣的:(hdu 4067 Random Maze)