poj 3422 Kaka's Matrix Travels 【最大费用最大流】【好题】

Kaka's Matrix Travels
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 8729 Accepted: 3498

Description

On an N × N chessboard with a non-negative number in each grid, Kaka starts his matrix travels with SUM = 0. For each travel, Kaka moves one rook from the left-upper grid to the right-bottom one, taking care that the rook moves only to the right or down. Kaka adds the number to SUM in each grid the rook visited, and replaces it with zero. It is not difficult to know the maximum SUM Kaka can obtain for his first travel. Now Kaka is wondering what is the maximum SUM he can obtain after his Kth travel. Note the SUM is accumulative during the K travels.

Input

The first line contains two integers N and K (1 ≤ N ≤ 50, 0 ≤ K ≤ 10) described above. The following N lines represents the matrix. You can assume the numbers in the matrix are no more than 1000.

Output

The maximum SUM Kaka can obtain after his Kth travel.

Sample Input

3 2
1 2 3
0 2 1
1 4 2

Sample Output

15



题意:给你一个N*N的矩阵,每个位置都有一定的点权,当你走到一个位置时,你可以获取该位置的点权。现在一个人要从左上角到右下角走K次,每次只能选择向下走或者向右走,问你走K次所能获得的最大权值和。 要求——每个点可以无限走,但点权只能获取一次。


我写了一个关于这类问题的讲解:点我


建图如下:设置超级源点source,超级汇点sink。

1,source向起点左点建边,容量为K,费用为0;

2,拆点,每个点拆为容量为1,费用为点权的边;

3,<u, v>可达关系建边u左->v左、u左->v右、u右->v左、u右->v右。边的的容量为INF,费用为0;

4,终点到sink建边,容量为K,费用为0。

最后跑一次最大费用最大流,结果就是最大权值和。




AC代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#define MAXN 5000+10
#define MAXM 1000000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], pre[MAXN];
bool vis[MAXN];
int source, sink;
int N, K;
int Map[60][60];
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
    Edge E1 = {u, v, w, 0, c, head[u]};
    edge[edgenum] = E1;
    head[u]= edgenum++;
    Edge E2 = {v, u, 0, 0, -c, head[v]};
    edge[edgenum] = E2;
    head[v]= edgenum++;
}
int point(int x, int y)
{
    return (x-1) * N + y;
}
void getMap()
{
    int t = N*N;
    source = 0, sink = 2*t+1;
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= N; j++)
        {
            scanf("%d", &Map[i][j]);
            addEdge(point(i, j), point(i, j) + t, 1, Map[i][j]);//拆点
            if(i < N)//四种情况
            {
                addEdge(point(i, j) + t, point(i+1, j), INF, 0);
                addEdge(point(i, j) + t, point(i+1, j) + t, INF, 0);
                addEdge(point(i, j), point(i+1, j), INF, 0);
                addEdge(point(i, j), point(i+1, j) + t, INF, 0);
            }
            if(j < N)//四种情况
            {
                addEdge(point(i, j) + t, point(i, j+1), INF, 0);
                addEdge(point(i, j) + t, point(i, j+1) + t, INF, 0);
                addEdge(point(i, j), point(i, j+1), INF, 0);
                addEdge(point(i, j), point(i, j+1) + t, INF, 0);
            }
        }
    }
    addEdge(source, point(1, 1), K, 0);
    addEdge(point(N, N) + t, sink, K, 0);
}
bool SPFA(int s, int t)
{
    queue<int> Q;
    memset(dist, -INF, sizeof(dist));
    memset(vis, false, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(dist[E.to] < dist[u] + E.cost && E.cap > E.flow)
            {
                dist[E.to] = dist[u] + E.cost;
                pre[E.to] = i;
                if(!vis[E.to])
                {
                    vis[E.to] = true;
                    Q.push(E.to);
                }
            }
        }
    }
    return pre[t] != -1;
}
void MCMF(int s, int t, int &cost, int &flow)
{
    cost = flow = 0;
    while(SPFA(s, t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            Edge E = edge[i];
            Min = min(Min, E.cap-E.flow);
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
}
int main()
{
    while(scanf("%d%d", &N, &K) != EOF)
    {
        init();
        getMap();
        int cost, flow;
        MCMF(source, sink, cost, flow);
        printf("%d\n", cost);
    }
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值