Kaka's Matrix Travels
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 10450 | Accepted: 4255 |
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
Source
POJ Monthly--2007.10.06, Huang, Jinsong
题目链接:http://poj.org/problem?id=3422
题目大意:一个n*n的矩阵,可从左上到右下走k次,每个位置有一个权值,只能取一次,求k次能取到的最大值
题目分析:k=1时普通dp,k=2时"多线程"dp,k>2时基础的费用流,对矩阵中的每个点拆点建图,由于每个点不限制访问次数,因此拆点完需要连两条边,一条容量为1,边权为点权;一条容量为INF,边权为0,每个点再往其右边和下面(如果存在)的入点连容量为INF,权值为0的边,注意源点和汇点的容量为k,然后跑费用流
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int const INF = 0x7fffffff;
int const MAX = 5005;
int n, k, src, sk;
int cnt, head[MAX], dis[MAX], pre[MAX], mp[55][55];
bool vis[MAX];
struct EDGE {
int to, nxt, cap, cost;
}e[MAX << 4];
void Init() {
src = 0;
sk = 2 * n * n + 1;
cnt = 0;
memset(head, -1, sizeof(head));
}
void Add(int u, int v, int cap, int cost) {
e[cnt].to = v;
e[cnt].nxt = head[u];
e[cnt].cap = cap;
e[cnt].cost = cost;
head[u] = cnt++;
e[cnt].to = u;
e[cnt].nxt = head[v];
e[cnt].cap = 0;
e[cnt].cost = -cost;
head[v] = cnt++;
}
void Build() {
Add(src, 1, k, 0);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &mp[i][j]);
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int p = n * (i - 1) + j;
Add(p * 2 - 1, p * 2, 1, -mp[i][j]);
Add(p * 2 - 1, p * 2, INF, 0);
if (j < n) {
Add(p * 2, (p + 1) * 2 - 1, INF, 0);
}
if (i < n) {
Add(p * 2, (p + n) * 2 - 1, INF, 0);
}
}
}
Add(2 * n * n, sk, k, 0);
}
bool SPFA() {
memset(vis, false, sizeof(vis));
for (int i = src; i <= sk; i++) {
dis[i] = INF;
}
queue<int> q;
dis[src] = 0;
vis[src] = true;
pre[src] = -1;
q.push(src);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (e[i].cap > 0 && dis[v] > dis[u] + e[i].cost) {
dis[v] = dis[u] + e[i].cost;
pre[v] = i;
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
return dis[sk] != INF;
}
int MCMF() {
int ans = 0;
while (SPFA()) {
int mi = INF;
for (int u = sk; u != src; u = e[pre[u] ^ 1].to) {
mi = min(mi, e[pre[u]].cap);
}
for (int u = sk; u != src; u = e[pre[u] ^ 1].to) {
e[pre[u]].cap -= mi;
e[pre[u] ^ 1].cap += mi;
ans += mi * e[pre[u]].cost;
}
}
return ans;
}
int main() {
scanf("%d %d", &n, &k);
Init();
Build();
printf("%d\n", -MCMF());
}