382. K取方格数(图论,费用流,拆点,上下界可行流,网格图模型)

在一个 N×N 的矩形网格中,每个格子里都写着一个非负整数。

可以从左上角到右下角安排 K 条路线,每一步只能往下或往右,沿途经过的格子中的整数会被取走。

若多条路线重复经过一个格子,只取一次。

求能取得的整数的和最大是多少。

输入格式

第一行包含两个整数 N 和 K。

接下来 N 行,每行包含 N 个不超过 1000 的整数,用来描述整个矩形网格。

输出格式

输出一个整数,表示能取得的最大和。

数据范围

1≤N≤50,
0≤K≤10

输入样例:
3 2
1 2 3
0 2 1
1 4 2
输出样例:
15

解析: 

本题我们需要在一个 n×n 的矩阵,每个格子上都写着一个非负整数,我们指定 k 条路线,每一步只能往下或往右,每个格子上的数只能取一次,求如何指定这 k 条路线,能让取得的整数和最大。

像这种方格取数问题,存在简单版本,如指定一条路线或两条路线的问题,可以使用线性 dp 来求,但是本题由于最多会有 10 条路径,用 dp 会超时,所以这里考虑用费用流来解决。

首先我们先不去考虑每个格子只能取一次的问题,我们先设法将路线转化成流。起点是左上角,终点是右下角,所有的路线都是从左上角走到右下角。所以我们可以建立一个源点和一个汇点,从源点向左上角连一条容量为 k 的边,从汇点向右下角连一条容量为 k 的边,表示有 k 条路线从左上角走到右下角。然后每个格子都可以向右和向下走,因此从每个格子向右和向下两个格子连边,且每个格子能走多次,没有限制,因此这些边的容量都是 +∞。这样我们就在网格图上建立了一个流网络。

此时可以发现流网络的任意一个可行流和原问题的任意一个方案都是一

方格问题也可以使用网络算法进行求解。以下是一个Python实现: ```python from collections import deque n = int(input()) grid = [] for i in range(n): row = list(map(int, input().split())) grid.append(row) # 计算每个格子的编号 id_map = [[0] * n for _ in range(n)] cnt = 0 for i in range(n): for j in range(n): id_map[i][j] = cnt cnt += 1 # 定义网络图的源、汇,以及每个格子的容量 s = 2*n*n t = 2*n*n + 1 cap = [[0] * (2*n*n+2) for _ in range(2*n*n+2)] for i in range(n): for j in range(n): cap[s][id_map[i][j]] = 1 # 源到每个格子的容量为1 cap[id_map[i][j]+n*n][t] = 1 # 每个格子到汇的容量为1 if i > 0: # 上方格子向当前格子的容量为上方格子的值 cap[id_map[i-1][j]][id_map[i][j]+n*n] = grid[i][j] if j > 0: # 左方格子向当前格子的容量为左方格子的值 cap[id_map[i][j-1]][id_map[i][j]+n*n] = grid[i][j] # 使用BFS算法找到增广路径 def bfs(): queue = deque([s]) visited = [False] * (2*n*n+2) visited[s] = True parent = [-1] * (2*n*n+2) while queue: u = queue.popleft() for v in range(2*n*n+2): if not visited[v] and cap[u][v] > 0: visited[v] = True parent[v] = u queue.append(v) if v == t: # 找到增广路径 path = [] while v != s: path.append(v) v = parent[v] path.append(s) path.reverse() return path return None # 使用Ford-Fulkerson算法求最大 max_flow = 0 while True: path = bfs() if not path: break flow = float('inf') for i in range(len(path)-1): u, v = path[i], path[i+1] flow = min(flow, cap[u][v]) for i in range(len(path)-1): u, v = path[i], path[i+1] cap[u][v] -= flow cap[v][u] += flow max_flow += flow print(max_flow) ``` 这个算法将每个格子看做一个节,将源和汇分别连接到每个格子。根据题目要求,每个格子只能向下或向右走,因此可以将每个格子成两个节,一个表示向下走,一个表示向右走。这样就可以将问题转化为一个网络问题。 具体来说,源向每个格子的向下走节连边,容量为1;每个格子的向右走节向汇连边,容量为1;每个格子的向下走节向下方格子的向上走节连边,容量为下方格子的值;每个格子的向右走节向左方格子的向右走节连边,容量为左方格子的值。 然后使用标准的Ford-Fulkerson算法求解最大,即可得到答案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值