题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1084

基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
 收藏
 关注
一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。

 
例如:3 * 3的方格。
 
1 3 3
2 1 3
2 2 1
 
能够获得的最大价值为:17。1 -> 3 -> 3 -> 3 -> 1 -> 2 -> 2 -> 2 -> 1。其中起点和终点的奖励只计算1次。
 
Input
第1行:2个数M N,中间用空格分隔,为矩阵的大小。(2 <= M, N <= 200)
第2 - N + 1行:每行M个数,中间用空格隔开,对应格子中奖励的价值。(1 <= A[i,j] <= 10000)
Output
输出能够获得的最大价值。
Input示例
3 3
1 3 3
2 1 3
2 2 1
Output示例
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的更多相关文章

  1. 51Nod 1084 矩阵取数问题 V2 双线程DP 滚动数组优化

    基准时间限制:2 秒 空间限制:131072 KB  一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上.第1遍时只能向下和向右走,第2遍时只能向 ...

  2. 1084 矩阵取数问题 V2

    1084 矩阵取数问题 V2 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下 ...

  3. 【Luogu】P2045方格取数加强版(最小费用最大流)

    题目链接 通过这题我学会了引诱算法的行为,就是你通过适当的状态设计,引诱算法按照你想要它做的去行动,进而达到解题的目的. 最小费用最大流,首先将点拆点,入点和出点连一条费用=-权值,容量=1的边,再连 ...

  4. [luoguP2045] 方格取数加强版(最小费用最大流)

    传送门 水题 ——代码 #include <queue> #include <cstdio> #include <cstring> #include <ios ...

  5. 51Nod 1084:矩阵取数问题 V2(多维DP)

    1084 矩阵取数问题 V2  基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励 ...

  6. 51nod1084 矩阵取数问题 V2

    O(n4)->O(n3)妈呀为什么跑这么慢woc #include<cstdio> #include<cstring> #include<cctype> #i ...

  7. 51Nod 1083 矩阵取数问题(矩阵取数dp,基础题)

    1083 矩阵取数问题 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下 ...

  8. 矩阵取数游戏 2007年NOIP全国联赛提高组(dp+高精)

    矩阵取数游戏 2007年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold     题目描述 Description [问题描述]帅帅经常跟 ...

  9. 51nod 1411 矩阵取数问题 V3

    给定一个m行n列的矩阵,你可以从任意位置开始取数,到达任意位置都可以结束,每次可以走到的数是当前这个数上下左右的邻居之一,唯一的限制是每个位置只能经过一次,也就是说你的路径不自交.所经过的数的总作为你 ...

随机推荐

  1. 关于js对象的基础使用方法-《javascript设计模式》读书笔记

    一.利用对象收编变量 当我们决定实现某一项功能的时候最简单的其实就是写一个命名函数,然后调用来实现,就像这样: function checkName(){ //验证姓名 } function chec ...

  2. Git可视化极简易教程 — Git GUI使用方法

    Git可视化极简易教程 — Git GUI使用方法 学习了:http://www.runoob.com/w3cnote/git-gui-window.html

  3. Java实现链表结构的具体代码

    一.数据准备 1. 定义节点 2.   定义链表 1.数据部分 2.节点部分 class DATA //数据节点类型 { String key; String name; int age; } cla ...

  4. centos DHCP

    yum install dhcp cat /usr/share/doc/dhcp-4.2.5/dhcpd.conf.example > /etc/dhcp/dhcpd.conf vim /etc ...

  5. eclipse 如何把java项目转成web项目

    经常在eclipse中导入web项目时,出现转不了项目类型的问题,导入后就是一个java项目. 解决步骤: 1.进入项目目录,可看到.project文件,打开. 2.找到<natures> ...

  6. 使用matlab进行mex编译时的路径问题mexopts

            matlab和vs 进行混合编程时总须要使用matlab编译mexFunction.cpp文件. 这些文件免不了使用include下的*.h和lib下的*.lib文件.举例说明.这次我 ...

  7. Timer与ScheduledExecutorService间的抉择

    java.util.Timer计时器有管理任务延迟执行("如1000ms后执行任务")以及周期性执行("如每500ms执行一次该任务").但是,Timer存在一 ...

  8. 用Putty连接Linux

    随着linux应用的普及,linux管理越来越依赖远程管理.在各种telnet类工具中,putty是其中最出色的一个. 一.Putty简介     Putty是一个免费小巧的Win32平台下的teln ...

  9. mysql日期格式转化

    select DATE_FORMAT( '20170701', '%Y-%m-%d'); 先挖坑

  10. Go 语言从新手到大神:每个人都会踩的五十个坑(转)

    Go语言是一个简单却蕴含深意的语言.但是,即便号称是最简单的C语言,都能总结出一本<C陷阱与缺陷>,更何况Go语言呢.Go语言中的许多坑其实并不是因为Go自身的问题.一些错误你再别的语言中 ...