51Nod 1084 矩阵取数问题 V2 —— 最小费用最大流 or 多线程DP
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1084


第1行:2个数M N,中间用空格分隔,为矩阵的大小。(2 <= M, N <= 200)
第2 - N + 1行:每行M个数,中间用空格隔开,对应格子中奖励的价值。(1 <= A[i,j] <= 10000)
输出能够获得的最大价值。
3 3
1 3 3
2 1 3
2 2 1
17
题解:
1.实际上就是求两条从左上角到右下角的路径的权值和最大(重叠部分只算一次)。
2.感觉上,最优情况下的两条路径是不会有重叠部分的。但这只是感觉,缺乏证明,但就算两条路径可以有重叠部分,问题也可以照样解决。
3.一拿到题目首先就想到了DP,但是因为有两条路径,如果进行两次DP,在第一次DP后,把路径上的值全部清零,然后再进行一次DP,所得的结果不一定是最优的。因此:两条路径必须同时考虑,而不能割裂开来。
4.但是当时真的想不出怎么个DP,于是自己就朝着暴力一点的方向去思考,于是想到了图论之网络流,结果还真行得通:
如图:
1) 把每个格子拆成两点,一个点作为入点,一个点作为出点,从入点向出点引两条边,第一条边的容量为1,单位花费为-a[i][j],第二条的容量为1,单位花费为0。
2) 对于每个格子,从出点引一条边向右边格子的入点,容量为2,单位花费为0。
3) 以第一个格子的入点为源点,以最后一个格子的出点为汇点,跑最小费用最大流,得到的最小花费的相反数,即为答案。
正确性证明:
1) 由于每相邻两个点直接的容量都为2,所以就满足了“两条路径”。
2) 在一个格子里,入点和出点之间,只有一条边(一个容量)的花费是有效的,这就满足了“路径重叠部分的值只取一次”。
3) 最小费用最大流:可知最大流为2,这就满足了“两条路径”,而最小费用,即为最大费用的相反数,所以满足了权值和最大。
多线程DP做法:
1.可以在DP的时候,把两条路径也考虑进去。
2.设dp[step][x1][x2]:为走了step步,第一条路径的当前x坐标未x1,第二条的x坐标为x2,知道步数和x坐标,就可以知道y坐标了。详情请看代码。
(此题用网络流居然跑得比DP还快)
最小费用最大流:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
#define ms(a,b) memset((a),(b),sizeof((a)))
using namespace std;
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+;
const int MAXN = 1e5+; struct Edge
{
int to, next, cap, flow, cost;
}edge[];
int tot, head[MAXN];
int pre[MAXN], dis[MAXN];
bool vis[MAXN];
int N; void init(int n)
{
N = n;
tot = ;
memset(head, -, sizeof(head));
} void add(int u, int v, int cap, int cost)
{
edge[tot].to = v; edge[tot].cap = cap; edge[tot].cost = cost;
edge[tot].flow = ; edge[tot].next = head[u]; head[u] = tot++;
edge[tot].to = u; edge[tot].cap = ; edge[tot].cost = -cost;
edge[tot].flow = ; edge[tot].next = head[v]; head[v] = tot++;
} bool spfa(int s, int t)
{
queue<int>q;
for(int i = ; i<=N; i++)
{
dis[i] = INF;
vis[i] = false;
pre[i] = -;
} dis[s] = ;
vis[s] = true;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i!=-; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap>edge[i].flow && dis[v]>dis[u]+edge[i].cost)
{
dis[v] = dis[u]+edge[i].cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
if(pre[t]==-) return false;
return true;
} int minCostMaxFlow(int s, int t, int &cost)
{
int flow = ;
cost = ;
while(spfa(s,t))
{
int Min = INF;
for(int i = pre[t]; i!=-; i = pre[edge[i^].to])
{
if(Min>edge[i].cap-edge[i].flow)
Min = edge[i].cap-edge[i].flow;
}
for(int i = pre[t]; i!=-; i = pre[edge[i^].to])
{
edge[i].flow += Min;
edge[i^].flow -= Min;
cost += edge[i].cost*Min;
}
flow += Min;
}
return flow;
} int a[][];
int main()
{
int n, m;
scanf("%d%d",&m,&n);
for(int i = ; i<n; i++)
for(int j = ; j<m; j++)
scanf("%d",&a[i][j]); int N = n*m;
int st = , en = *N-;
init(*N); for(int i = ; i<n; i++)
for(int j = ; j<m; j++)
{
/*把格子拆成两个点,一个点进,一个点出,中间有两条边:
一条边的容量为1,单位花费为-a[i][j],
另一条的容量为1,单位花费为0.
这样就使得任意一个格子,可以在两条路径上,但只能被计算一次
*/
add(i*m+j,N+i*m+j,,-a[i][j]);
add(i*m+j,N+i*m+j,,); if(j!=m-) add(N+i*m+j,i*m+j+,,); //引向右边的格子
if(i!=n-) add(N+i*m+j,(i+)*m+j,,); //引向下边的格子
} int ans;
minCostMaxFlow(st,en,ans);
printf("%d\n",-ans); //取反
}
多线程DP:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
using namespace std;
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int MOD = 1e9+;
const int MAXN = 1e6+; int a[][], dp[][][];
int main()
{
int n, m;
while(scanf("%d%d",&m,&n)!=EOF)
{
for(int i = ; i<n; i++)
for(int j = ; j<m; j++)
scanf("%d",&a[i][j]); memset(dp, , sizeof(dp));
dp[][][] = a[][];
for(int step = ; step<=n+m-; step++)
for(int x1 = ; x1<=min(n-,step); x1++)
for(int x2 = ; x2<=min(n-,step); x2++)
{
int y1 = step-x1;
int y2 = step-x2;
int flag = (x1!=x2)||(y1!=y2); //判断两条路径的当前位置是否重叠
int t1 = , t2 = , t3 = , t4 = ;
if(x1!=&&x2!=) t1 = dp[step-][x1-][x2-];
if(x1!=&&y2!=) t2 = dp[step-][x1-][x2];
if(y1!=&&x2!=) t3 = dp[step-][x1][x2-];
if(y1!=&&y2!=) t4 = dp[step-][x1][x2];
dp[step][x1][x2] = max(max(t1,t2),max(t3,t4))+a[x1][y1]+flag*a[x2][y2];
} printf("%d\n", dp[n+m-][n-][n-]);
}
}
51Nod 1084 矩阵取数问题 V2 —— 最小费用最大流 or 多线程DP的更多相关文章
- 51Nod 1084 矩阵取数问题 V2 双线程DP 滚动数组优化
基准时间限制:2 秒 空间限制:131072 KB 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上.第1遍时只能向下和向右走,第2遍时只能向 ...
- 1084 矩阵取数问题 V2
1084 矩阵取数问题 V2 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下 ...
- 【Luogu】P2045方格取数加强版(最小费用最大流)
题目链接 通过这题我学会了引诱算法的行为,就是你通过适当的状态设计,引诱算法按照你想要它做的去行动,进而达到解题的目的. 最小费用最大流,首先将点拆点,入点和出点连一条费用=-权值,容量=1的边,再连 ...
- [luoguP2045] 方格取数加强版(最小费用最大流)
传送门 水题 ——代码 #include <queue> #include <cstdio> #include <cstring> #include <ios ...
- 51Nod 1084:矩阵取数问题 V2(多维DP)
1084 矩阵取数问题 V2 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励 ...
- 51nod1084 矩阵取数问题 V2
O(n4)->O(n3)妈呀为什么跑这么慢woc #include<cstdio> #include<cstring> #include<cctype> #i ...
- 51Nod 1083 矩阵取数问题(矩阵取数dp,基础题)
1083 矩阵取数问题 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下 ...
- 矩阵取数游戏 2007年NOIP全国联赛提高组(dp+高精)
矩阵取数游戏 2007年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description [问题描述]帅帅经常跟 ...
- 51nod 1411 矩阵取数问题 V3
给定一个m行n列的矩阵,你可以从任意位置开始取数,到达任意位置都可以结束,每次可以走到的数是当前这个数上下左右的邻居之一,唯一的限制是每个位置只能经过一次,也就是说你的路径不自交.所经过的数的总作为你 ...
随机推荐
- 【Salvation】——项目进展&已取得的成果
写在前面:这个项目为原创团体项目,其中美术设计与部分关卡功能为其他成员完成,我负责的部分以角色动画和登录注册为主. 一.游戏美术设计 游戏背景,道具,动物,人物帧动画制作全部完成. 1.人物 2.游戏 ...
- Android--数据库数据显示至屏幕
MainActivity.java 这段代码的作用是从数据库中获取到数据并显示在界面上 import java.util.ArrayList; import java.util.List; impor ...
- Ant Design 3.0 使用案例
代码地址如下:http://www.demodashi.com/demo/12309.html 本文适合对象 有过React使用经验. 有过webpack使用经验. 了解node. DEMO使用方式 ...
- SVN客户端忽略无关文件
修改前请先备份文件 ~/.subversion/config. 1,打开Terminal,输入命令: $ open ~/.subversion/config 2,在打开的文件中寻找:`global ...
- iPhone换电池是原装电池好还是换第三方大容量电池好?
转:https://www.xianjichina.com/news/details_60791.html 最近这段时间苹果降速门事件持续发酵,闹得满城风雨.尽管苹果公司两次致歉,很多果粉都去更换电池 ...
- java中类型的隐式转换
byte+byte=int,低级向高级是隐式类型转换,高级向低级必须强制类型转换,byte<char<short<int<long<float<double
- java中Executor、ExecutorService、ThreadPoolExecutor介绍
源码非常简单,只有一个execute(Runnable command)回调接口 public interface Executor { /** * Executes the given c ...
- 32.10 使用模板更改控件的UI
32.10 使用模板更改控件的UI 样式是改变WPF控件基本外形的非常好(且非常简单)的方式,它通过为窗口部件的特性设置建立一组默认的值,从而改变WPF控件的基本外形.但是,即使样式允许我们改变各种 ...
- C#之stream
在C#中经常要用stream stream下面主要有 FileStream:使用文件作为后备设备. BufferedStream:使用缓冲区作为后备设备,用来增强性能的中间存储. MemoryStre ...
- redis启动错误-- Creating Server TCP listening socket *:6379: listen: UnKnown error
前提:windows server 2008.redis 3.x 今天给服务器部署redis环境,文件配置.服务安装都很顺利,可就在启动服务的时候提示 百度老半天也没找到个说到点子上的. 这里记录下解 ...