PTA——寻宝路线

题目描述

在一个m行n列方格矩阵中,每一个方格内摆放着价值不等的宝贝(价值可正可负),让小明感到好奇的是,从左上角到达右下角的所有可能路线中,能捡到宝贝的价值总和最大是多少?而且这种达到最大值的路线
又有多少条?【注意:只能从一个格子向下或向右走到相邻格子,并且走到的格子宝贝一定会被捡起。】

输入格式:
第一行为整数m,n(均不大于100),下一行开始会有一个m行n列的整数方阵,对应方格矩阵中的宝贝价值(这些值的绝对值都不超过500)。

输出格式:
单独一行输出2个整数,分别为能捡到宝贝价值总和的最大值和达到最大值的路线数量,2个整数间隔一个空格。

输入样例:
在这里给出一组输入。例如:

4  5
2  -1  6  -2  9
-3  2  5  -5  1
5   8  3  -2  4
5   2  8  -4  7

输出样例:
对应的输出为:26 3

题目分析

一:要求出路径最大的价值和,以及到得到最大价值的走法有几种。可以将两者分开来存,用d数组记录最大价值,用v数组记录在当前点能取到最大值的路径的条数(当然dp计算v数组时要参考d数组中的价值数)。

二:此题最大价值和路径亮点在于:因为存在负数,所以对于边界情况要做出限制,防止路径为负时取到边界上的0。(如题中样例第三行,若不做限制,取路径时它会选择左边的0,而不是上方的-1,但这样就不是从左上角开始的路径了)。有两种方法解决:
法一:对d数组边框进行初始化成不可能取到的最小数(如下初始化为-1000)。更简洁
法二:在dp过程中用if,else语句对边界进行限制。

最大价值是经典的dp套路了,在当前点对左和上路径最大值比较再取最大即可(只能向下或向右走)
对于路径的dp(见下例)
(num—宝贝价值方格矩阵)

000
011
031
011

(d—初始化如下图)

00-1000
000
-100000
-100000

(v—初始化如下图,取v[0][1]=1)

010
000
000
000

对于几种情况的分析说明

一:从num[1][1]开始出发
价值:d[1][1]=max(d[0][1],d[1][0])+num[1][1]=1 (因为一定会用到这两个格子走出第一步,因此初始化为0)
路径:因为d[0][1]==d[1][0]==0,路径v[1][1]=v[0][1]+v[1][0](第一步用到的两个格子数值一定相等,因此初始化一个格子为1即可,求完表示目前路径条数为1)

(d—走到(2,2)格子)

00-1000
012
-100040
-100000

(v—走到(2,2)格子)

010
011
010
000

二:对于d[2][2]格子
价值:因为左边d[2][1]==4>上方d[1][2]==2,因此取d[2][2]=4+num[2][2]=5
路径:v记录取到最大价值和的路径条数,因为左边>上方,所以从左边走过来才能取到最大价值。v[2][2]应该记录从左边过来的路径的条数,因此要取v[2][2]=v[2][1]=1

(d—走到(3,2)格子)

00-1000
012
-100045
-100050

(v—走到(3,2)格子)

010
011
011
010

三:对于d[3][2]格子,到这步时,左边和上方的格子数都为5,相等
价值:两边相等,任取一边加上当前价值即可.取d[3][2]=d[2][2]+num[3][2]=6
路径:两边相等的情况说明取到当前最大的路径从左边和上方都可以,因此要对两个方向的路径数求和。v[3][2]=v[2][2]+v[3][1]=2

(d—最终遍历完结果)

00-1000
012
-100045
-100056

(v—最终遍历完结果)

010
011
011
012

实现代码

#include<iostream>
using namespace std;
int num[110][110];
int d[110][110], v[110][110];
int main()
{
	int m, n;
	cin >> m >> n;
	for (int i = 2; i <= m; i++)//初始化d(记录价值),v(记录路径条数)数组
		d[i][0] = -1000;
	for (int i = 2; i <= n; i++)
		d[0][i] = -1000;
	v[0][1] = 1;//或v[1][0]=1也可,因为会经历v[1][1]=v[0][1]+v[1][0]过程,初始化两者任一都可
	for (int i = 1; i <= m; i++)
		for (int j = 1; j <= n; j++)
			cin >> num[i][j];
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			d[i][j] = max(d[i - 1][j], d[i][j - 1]) + num[i][j];
			if (d[i - 1][j] > d[i][j - 1])//来自上方的价值和更大,取路径数为走到上方的路径条数(这步就是从是上方顺着走,没有步数的增加)
				v[i][j] = v[i - 1][j];
			else if (d[i - 1][j] < d[i][j - 1])//来自左方的价值和更大,取路径数目来自上方
				v[i][j] = v[i][j - 1];
			else
				v[i][j] = v[i - 1][j] + v[i][j - 1];//两边价值和相等,从两边走都行,因此路径为两者之和(即意为以取最大价值和的走法走到当前路径有从上,从左路径和条路)
		}
	}
	cout << d[m][n] << ' ' << v[m][n] << '\n';
	return 0;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值