问题描述:
就是 有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;
}