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;
}