poj 2396 Budget(有源汇上下界可行流)

这题的建图真恶心。好不容易写完后结果还wa了,顿时生无可恋啊,看了下discuss,说不能输出负数,最后不能输出多余的空行,我把这俩都改了,就AC了,虽说那里说这是在zoj不能ac的原因,不过在poj也适用,哈哈。
思路:每行为一个点,每列为一个点,给出的限制就是行列之间连线的流的上下界,初始下届为0,上界为INF。然后建好图之后,建立一个虚拟源点和虚拟汇点,源点和边连线,流量上下界为行的和,列和汇点连线,流量上下界为列的和。然后汇点向源点连接一条下界为0,上界INF的线,这就成了一个 无源汇上下界可行流 的问题。

#include <queue>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MAXN = 300;
const int INF = 0x7f7f7f7f;
struct edge
{
    edge() {}
    edge(int _to, int _cap, int _rev, int _id)
        :to(_to),cap(_cap),rev(_rev),id(_id){}
    int to,cap,rev,id;
};
vector<edge> G[MAXN];
int level[MAXN];//分层
int iter[MAXN];//当前弧优化

void debug(int x)
{
    printf("%d\n",x);
}

void addedge(int from, int to, int cap, int id)
{
//    printf("%d ---- %d:%d\n",from,to,cap);
    G[from].push_back(edge(to, cap, G[to].size(), 0));
    G[to].push_back(edge(from, 0, G[from].size()-1, id));
}

//先分层,然后在分层图上dfs
void bfs(int s)
{
    memset(level, -1, sizeof(level));
    queue<int> que;
    level[s] = 0;
    que.push(s);
    while(!que.empty())
    {
        int v = que.front();
        que.pop();
        for(int i = 0; i < G[v].size(); ++i)
        {
            edge& e = G[v][i];
            if(e.cap > 0 && level[e.to] < 0)
            {
                level[e.to] = level[v] + 1;
                que.push(e.to);
            }
        }
    }
}

int dfs(int v, int t, int f)
{
    if(v == t) return f;
    for(int &i = iter[v]; i < G[v].size(); ++i)
    {
        edge &e = G[v][i];
        if(e.cap > 0 && level[v] < level[e.to])
        {
            int d = dfs(e.to, t, min(f, e.cap));
            if(d > 0)
            {
//                printf("%d->%d:%d\n",v,e.to,e.cap);
//                debug(e.cap);
                e.cap -= d;
//                debug(e.cap);
//                debug(G[e.to][e.rev].cap);
                G[e.to][e.rev].cap += d;
//                debug(G[e.to][e.rev].cap);
                return d;
            }
        }
    }
    return 0;
}

int maxFlow(int s, int t)
{
    int flow = 0;
    while(true)
    {
        bfs(s);
        if(level[t] < 0) return flow;
        memset(iter,0,sizeof(iter));
        int f;
        while((f = dfs(s,t,INF)) > 0)
            flow += f;
    }
}

//这里n表示行数,m表示列数
int n,m,csa,s,t,S,T,sum;
int row[MAXN],col[MAXN];
int low[MAXN][MAXN];
int up[MAXN][MAXN];
int totFlow[MAXN];
int res[MAXN][MAXN];
bool flag = false;

void process(int r, int c, char ch, int ct)
{
    if(r == 0 && c == 0)
    {
        if(ch == '>')
        {
            for(int i = 1; i <= n; ++i)
                for(int j = 1; j <= m; ++j)
                    low[i][j] = max(low[i][j],ct+1);
        }
        else if(ch == '<')
        {
            for(int i = 1; i <= n; ++i)
                for(int j = 1; j <= m; ++j)
                    up[i][j] = min(up[i][j],ct-1);
        }
        else
        {
            for(int i = 1; i <= n; ++i)
                for(int j = 1; j <= m; ++j)
                    low[i][j] = up[i][j] = ct;
        }
    }
    else if(r == 0)
    {
        if(ch == '>')
        {
            for(int i = 1; i <= n; ++i)
                low[i][c] = max(low[i][c],ct+1);
        }
        else if(ch == '<')
        {
            for(int i = 1; i <= n; ++i)
                up[i][c] = min(up[i][c],ct-1);
        }
        else
        {
            for(int i = 1; i <= n; ++i)
                low[i][c] = up[i][c] = ct;
        }
    }
    else if(c == 0)
    {
        if(ch == '>')
        {
            for(int i = 1; i <= m; ++i)
                low[r][i] = max(low[r][i],ct+1);
        }
        else if(ch == '<')
        {
            for(int i = 1; i <= m; ++i)
                up[r][i] = min(up[r][i],ct-1);
        }
        else
        {
            for(int i = 1; i <= m; ++i)
                low[r][i] = up[r][i] = ct;
        }
    }
    else//都不是0
    {
        if(ch == '>')
            low[r][c] = max(low[r][c],ct+1);
        else if(ch == '<')
            up[r][c] = min(up[r][c],ct-1);
        else
            low[r][c] = up[r][c] = ct;
    }

}

void read()
{
    scanf("%d %d",&n,&m);
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            low[i][j] = 0;
            up[i][j] = INF;
        }
    }
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d",&row[i]);
        if(row[i] < 0) flag = true;
    }
    for(int i = 1; i <= m; ++i)
    {
        scanf("%d",&col[i]);
        if(col[i] < 0) flag = true;
    }
    scanf("%d",&csa);
    char ch;
    int r,c,ct;
    for(int i = 0; i < csa; ++i)
    {
        scanf(" %d %d %c %d",&r,&c,&ch,&ct);
        if(ch == '<' && ct < 0) flag = true;
        process(r,c,ch,ct);
    }
    if(flag) return;
    //建图
    //虚拟源点和汇点
    s = 0,t = n+m+1;
    //超级虚拟原点和汇点
    S = t+1,T = t+2;
    addedge(t,s,INF,0);//下界为0,不用管
    //ok 在这里已经建好了行和列之间的边
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            addedge(i,j+n,up[i][j]-low[i][j],i*1000+j);
            totFlow[i] -= low[i][j];
            totFlow[j+n] += low[i][j];
        }
    }
    //下边就是虚拟源点和虚拟汇点了
    for(int i = 1; i <= n; ++i)
        addedge(s,i,0,0),totFlow[i] += row[i],totFlow[s] -= row[i];//上下界都为row[i]
    for(int i = 1; i <= m; ++i)
        addedge(i+n,t,0,0),totFlow[i+n] -= col[i],totFlow[t] += col[i];//上下界都为col[i]

    //以上就成了一个无源汇上下界循环流的网络
    //下边添加补流,增加必要边
    sum = 0;
    for(int i = s; i <= t; ++i)
    {
        if(totFlow[i] < 0)
            addedge(i,T,-totFlow[i],0);
        else
        {
            sum += totFlow[i];
            addedge(S,i,totFlow[i],0);
        }
    }
}

void solve()
{
    if(flag)
    {
        puts("IMPOSSIBLE");
        return;
    }
    int ret = maxFlow(S,T);
    int r,c;
    if(ret == sum)
    {
        for(int i = 1; i <= n+m; ++i)
        {
            for(int j = 0; j < G[i].size(); ++j)
            {
                if(G[i][j].id)
                {
                    r = G[i][j].id/1000;
                    c = G[i][j].id%1000;
                    res[r][c] += (low[r][c]+G[i][j].cap);
                }
            }
        }
        for(int i = 1; i <= n; ++i)
        {
            for(int j = 1; j <= m; ++j)
                printf("%d ",res[i][j]);
            printf("\n");
        }
    }
    else
        puts("IMPOSSIBLE");
}

void init()
{
    flag = false;
    memset(totFlow,0,sizeof(totFlow));
    memset(res,0,sizeof(res));
}

void Clear()
{
    for(int i = 0; i <= T; ++i)
        G[i].clear();
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        read();
        solve();
        Clear();
        if(T) printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值