HDU 4067 Random Maze

题意:给出一个有n个点m条边的有向图,现在要从图中删去一些边使得图满足以下条件:
1.图中只有一个入口(s)和一个出口(t)
2.所以边都是单向的(这个原图已经保证了)
3.对入口(s)而言,其出度=入度+1
4.对出口(t)而言,其入度=出度+1
5.对入口(s)、出口(t)以外的点,其入度=出度

而对图中每条边,保留这条边或者删去这条边都有相应的花费,分别为a、b,求使得该图满足条件的最小花费。


解法:
重新为每个边建立权值!
1.如果有边(u,v,a,b) 并且a<=b,那么建立边(v,u,1,b-a) sum+=a,如果要恢复此边需花费 b-a 的费用;in[u]++,out[v]++;
2.如果有边(u,v,a,b) 并且a>b,那么建立边(u,v,1,a-b) sum+=b,如果要删除此边需花费 a-b 的费用;
3.另需t和s 建立一条虚边,in[s]++,out[t]++;
最后建立一个超级源点S 和超级汇点T,则如果对于点 i in[i]>out[i] 那么建立边(S,i,in[i]-out[i],0);
相反 建立边(i,T,out[i]-in[i],0);
最小费用最大流后,如果max_flow=到汇点t所有容量只和 答案即为:sum+max_flow;

否则 impossible;

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>
#define CLR(x,y) memset(x,y,sizeof(x))
#define mp(x,y) make_pair(x,y)
#define eps 1e-9
#define INF 0x3f3f3f3f

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef complex<ld> point;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int M=5010;
const int N=111;

int in[N];          //入度和出度的差
int n,m,s,t;

struct Graph
{
    struct node
    {
        int v,next,w,flow;
        node() {};
        node(int a,int b,int c,int d)
        {
            next=a;
            v=b;
            w=c;
            flow=d;
        }
    } E[2*M];
    int head[N],pre[N],dis[N];
    int beg,end,flow,cost;
    bool h[N];
    int path[N];
    int NE,NV;
    void resize(int n)
    {
        this->NV=n;
    }
    void init(int n)
    {
        NE=0;
        NV=n;
        memset(head,-1,sizeof(int)*(n+10));
    }
    void insert(int u,int v,int flow,int w)
    {
        E[NE]=node(head[u],v,w,flow);
        head[u]=NE++;
        E[NE]=node(head[v],u,-w,0);
        head[v]=NE++;
    }
    bool update(int u,int v,int w)
    {
        if(dis[u]+w<dis[v])
        {
            dis[v]=dis[u]+w;
            return true;
        }
        return false;
    }
    bool spfa()
    {
        CLR(pre,-1);
        CLR(h,0);
        for(int i=0; i<=NV; i++)
            dis[i]=INF;
        dis[beg]=0;
        queue<int> q;
        q.push(beg);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            h[u]=0;
            for(int i=head[u]; i!=-1; i=E[i].next)
            {
                int v=E[i].v;
                if(E[i].flow>0&&update(u,v,E[i].w))
                {
                    pre[v]=u;
                    path[v]=i;
                    if(!h[v])
                    {
                        h[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        if(pre[end]==-1)
            return false;
        return true;
    }
    int mincost_maxflow(int s,int t)
    {
        this->beg=s;
        this->end=t;
        flow=0,cost=0;
        while(spfa())
        {
            int Min=INT_MAX;
            for(int i=end; i!=beg; i=pre[i])
                if(Min>E[path[i]].flow)
                    Min=E[path[i]].flow;
            for(int i=end; i!=beg; i=pre[i])
            {
                E[path[i]].flow-=Min;
                E[path[i]^1].flow+=Min;
            }
            flow+=Min;
            cost+=dis[end];
        }
        return cost;
    }
} g;


int main()
{
    int T,cas=1;
    read(T);
    while(T--)
    {
        read(n),read(m),read(s),read(t);
        g.init(n+2);
        memset(in,0,sizeof(in));
        in[s]++,in[t]--;
        int u,v,a,b,ss=0,tt=n+1,sum=0,cnt=0;
        for(int i=1;i<=m;i++)
        {
            read(u),read(v),read(a),read(b);
            if(a>=b)
            {
                g.insert(u,v,1,a-b);        //  删除(如果流过则不删)
                sum+=b;
            }
            else
            {
                g.insert(v,u,1,b-a);        //  不删除(如果流过则删除,加一条反边中和入度出度)
                sum+=a;
                in[u]--,in[v]++;
            }
        }
        for(int i=1;i<=n;i++)               //  保留入度,计算出度
        {
            if(in[i]>0)
                g.insert(ss,i,in[i],0);
            else
            {
                g.insert(i,tt,-in[i],0);
                cnt+=-in[i];
            }
        }
        sum+=g.mincost_maxflow(ss,tt);
        printf("Case %d: ",cas++);
        if(g.flow==cnt)
            write(sum),putchar('\n');
        else
            puts("impossible");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值