洛谷 P2045 方格取数加强版 (费用流)
来源:互联网 发布:ubuntu清空文件夹 编辑:程序博客网 时间:2024/06/09 21:05
前言
我本来想着很快做对本题的,结果做了将近一个下午,WA了四次,才换来难得的AC,看看还是有总结的必要,从中吸取教训。
题目描述
给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大
输入输出格式
输入格式:
第一行两个数n,k(1<=n<=50, 0<=k<=10)
接下来n行,每行n个数,分别表示矩阵的每个格子的数
输出格式:
一个数,为最大和
输入输出样例
输入样例#1:
3 1
1 2 3
0 2 1
1 4 2
输出样例#1:
11
说明
每个格子中的数不超过100
分析
这题要求一共走k次,即每个格子最多走k次,每个格子的价值最多获得一次。像这种限制点或边使用次数的题,我们往往将其转化为限制网络中边的流量(点要拆分)来解。纵观题目,n不到50,用网络流的信念应该更加坚定。然而使得到的价值最大,肯定是用最小费用最大流的变体——“最大费用最大流”来求解。凡是在图上走以求得到最大值的题,一般可以考虑dp或”最长路径”之类的东西,但边取的次数(也可能是点)有限制,就可以排除最短路,转而转化成费用流的问题了。
网络流的题难在难以看出来它是网络流。
费用流的模板一套,我们很快搞出最大费用最大流来。但构图才是网络流的最大的难点。像这种点的流量有限制的题,必然要拆点,然而我这个菜鸡居然naive的认为不拆点也可以,于是在我几次WA后居然改成了不拆点的构图=。=,直到最后才恍然大悟,真是too young。
我们将每个点拆成入点和出点,入点向出点连两条边:一条容量为1,花费为格子的价值,另一条容量为k-1,花费为0。然后按题目构图,从一个点的出边连向另一个点的入边,容量为k,费用为0。最后构出source和sink,向左上和右下连边,容量为k,费用为0。最后跑最大费用流就行了。
其实讲真挺简单,但是不注重细节带来的就是难忘的WA。
代码:
#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <queue>#include <algorithm>#define N 60#define INF 0x7fffffffusing namespace std;queue <int> q;bool vis[N*N*2];int n, k, cur = -1, head_p[N*N*2], val[N][N];int s, t, flow[N*N*2], pre_e[N*N*2], pre_v[N*N*2], dis[N*N*2];struct Tadj{ int next, obj, cap, cost; Tadj() {} Tadj(int next, int obj, int cap, int cost): next(next), obj(obj), cap(cap), cost(cost) {}}Edg[N*N*16];void Insert(int a, int b, int c, int d){ cur ++; Edg[cur] = Tadj(head_p[a], b, c, d); head_p[a] = cur;}bool Spfa(){ for(int i = s; i <= t; i++) dis[i] = -INF, vis[i] = false; q.push(s); dis[s] = 0; vis[s] = true; flow[s] = INF; while(!q.empty()){ int now = q.front(); q.pop(); for(int i = head_p[now]; ~ i; i = Edg[i].next){ int v = Edg[i].obj, c = Edg[i].cap, l = Edg[i].cost; if(!c) continue; if(dis[v] < dis[now] + l){ dis[v] = dis[now] + l; flow[v] = min(flow[now], c); pre_v[v] = now; pre_e[v] = i; if(!vis[v]){ q.push(v); vis[v] = true; } } } vis[now] = false; } return dis[t] != -INF;}int Max_cost_Max_flow(){ int cost = 0; while(Spfa()){ cost += dis[t] * flow[t]; for(int i = t; i != s; i = pre_v[i]){ Edg[pre_e[i]].cap -= flow[t]; Edg[pre_e[i]^1].cap += flow[t]; } } return cost;}int main(){ scanf("%d%d", &n, &k); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) scanf("%d", &val[i][j]); s = 0; t = n * n * 2 + 1; for(int i = s; i <= t; i++) head_p[i] = -1; Insert(s, 1, k, 0); Insert(1, s, 0, 0); Insert(n*n*2, t, k, 0); Insert(t, n*n*2, 0, 0); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++){ Insert((i-1)*n+j, (i-1)*n+j+n*n, 1, val[i][j]); Insert((i-1)*n+j+n*n, (i-1)*n+j, 0, -val[i][j]); Insert((i-1)*n+j, (i-1)*n+j+n*n, k-1, 0); Insert((i-1)*n+j+n*n, (i-1)*n+j, 0, 0); } for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++){ if(i < n){ Insert((i-1)*n+j+n*n, i*n+j, k, 0); Insert(i*n+j, (i-1)*n+j+n*n, 0, 0); } if(j < n){ Insert((i-1)*n+j+n*n, (i-1)*n+j+1, k, 0); Insert((i-1)*n+j+1, (i-1)*n+j+n*n, 0, 0); } } printf("%d\n", Max_cost_Max_flow()); return 0;}
既然目标是地平线,留给世界的只能是背影。
- 洛谷 P2045 方格取数加强版 (费用流)
- 洛谷P2045:方格取数加强版
- 方格取数加强版
- luogu2045 方格取数加强版
- 【费用流】codevs1227 方格取数 2
- 【codevs1227】方格取数2,费用流
- codevs1227(方格取数费用流)
- Codevs 1227方格取数 (费用流
- 1227 方格取数 2 费用流
- HDU 3820 Golden Eggs 方格取数加强版
- 【费用流】疯狂的方格取数 vijos 1653
- 【codevs1227】方格取数2 费用流(EK)
- 【CODEVS1227】方格取数2(费用流)
- Codevs 1227 方格取数2 [费用流] [拆点]
- 【codevs 1227】方格取数2(费用流)
- Codevs 1227 方格取数 2(费用流)
- Codevs[1227]方格取数2 费用流
- wiki 方格取数2 网络流 最大流最小费用流
- 求解最大子数组
- 欢迎使用CSDN-markdown编辑器
- HDU 1284 钱币兑换问题 完全背包
- vb.net 教程 3-2 窗体编程之窗体 1
- POJ 1465 Multiple(BFS+同余剪枝)
- 洛谷 P2045 方格取数加强版 (费用流)
- tld xml cvc-complex-type.2.4.a: Invalid content was found starting with element
- 一行代码解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10
- Mina Iobuffer中常用方法与介绍
- windows下安装ipython实战及问题解决
- web前端学习笔记---scrollWidth,clientWidth,offsetWidth的区别
- Spark 2.1.0官方文档翻译
- Python学习笔记-表格数据
- ElasticSearch5.3安装Head插件