poj 3422 费用流 拆点

问题描述:

 就是 有N*N的方阵 有一个人 要从(1,1) 走到(n,n) 只能向右或者向下  每个位置都有金钱。。。 当然 他走过之后 就 为0 了、、。  问他要走K次。。 最多能拿多少钱。。 

解析:

 果断的最大费用网络流啊。。。  将一个点 分成两个点   之间 连一条 容量为1 的 费用为 金钱数的费用

 再连一条 容量为k-1费用为0的 边。。。 之后要是 两个位置能想走 就连一条边。。。

#include <iostream>
using namespace std;
const int MAXN = 5000 + 10;
const int MAXE = 20000 + 50;
int map[MAXN][MAXN];    // map[i][j] = t:点i和点j之间的边权为t
int adj[MAXN];          // adj[u] = k:从点u出发的第一条边的编号为k
int pre[MAXN];          // pre[v] = k:在增广路上,到达点v的边的编号为k
int dis[MAXN];          // dis[u] = d:从起点s到点u的路径长为d
int que[MAXN];          // 模拟队列
bool inq[MAXN];         // inq[u]:点u是否在队列中
int e;                  // 拆点建图后总的边数
int ans;                // 最终答案:从(1,1)到(n,n)走k次后所能得到的最大值
struct EDGE
{
    int u;
    int v;
    int cap;      
    int cost;
    int next;
};
EDGE edges[MAXE];       // 图
// 加入新边u-->v:容量为cap,费用为cost
void _AddEdge(int u, int v, int cap, int cost)
{
    edges[e].u = u;
    edges[e].v = v;
    edges[e].cap = cap;
    edges[e].cost = cost;
    edges[e].next = adj[u];
    adj[u] = e;
    e++;
}
void AddEdge(int u, int v, int cap, int cost)
{
    _AddEdge(u, v, cap, cost);
    _AddEdge(v, u, 0, -cost);
}
// 在源点s和汇点t之间找一条增广路
bool Spfa(int s, int t)
{
    int u, v;
    int head, tail;
    // 初始化
    head = 0;
    tail = 1;
    memset(inq, false, sizeof(inq));
    memset(pre, -1, sizeof(pre));
    memset(dis, -1, sizeof(dis));
    que[head] = s;
    inq[s] = true;
    dis[s] = 0;
    while (tail != head)
    {
        u = que[head];
        inq[u] = false;
        // 遍历从点u出发的边: 编号为k的边,u--->v
        for (int k=adj[u]; k!=-1; k=edges[k].next)
        {
            v = edges[k].v;
            // 最大费用流
            if (edges[k].cap>0 && dis[v]<dis[u]+edges[k].cost)
            {
                dis[v] = dis[u] + edges[k].cost;
                pre[v] = k;
                if (!inq[v])
                {
                    inq[v] = true;
                    que[tail++] = v;
                    if (tail == MAXN)
                    {
                        tail = 0;
                    }
                }
            }
        }
        head++;
        if (head == MAXN)
        {
            head = 0;
        }
    }
    return dis[t] > 0;
}
void Edmonds_Karp(int s, int t)
{
    for (int k=pre[t]; k!=-1; k=pre[edges[k].u])
    {
        // 更新正向流量
        edges[k].cap -= 1;
        // 更新反向流量
        edges[k^1].cap += 1;   // 异或^:偶数加1,奇数减1
        ans += edges[k].cost;
    }
}
void Input(int &n, int &k)
{
    scanf("%d%d", &n, &k);
    for (int i=1; i<=n; ++i)
    {
        for (int j=1; j<=n; ++j)
        {
            scanf("%d", &map[i][j]);
        }
    }
}
// 拆点建图
void CreateGraph(int n, int k)
{
    // 拆点:化点为边
    for (int i=1; i<=n; ++i)
    {
        for (int j=1; j<=n; ++j)
        {
            int u = (i-1) * n + j;
            int v = u + n * n;   
            AddEdge(u, v, 1, map[i][j]);
           AddEdge(u, v, k, 0);
            // 向下走
            if (i < n)
            {
                AddEdge(v, u+n, k, 0);
            }
            // 向右走
            if (j < n)
            {
                AddEdge(v, u+1, k, 0);
            }
        }
    }
    int _n = 2 * n * n + 1;
    AddEdge(0, 1, k, 0);
    AddEdge(2*n*n, _n, k, 0);
}
int main()
{
    int n, k;
    e = 0; ans = 0;
    memset(adj, -1, sizeof(adj));
    Input(n, k);
    CreateGraph(n,k); 
    int _n = 2 * n * n + 1;
    while (Spfa(0, _n))
    {
        Edmonds_Karp(0, _n);
    }
    printf("%d\n", ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值