Filthy Rich
Problem Description
They say that in Phrygia, the streets are paved with gold. You’re currently on vacation in Phrygia, and to your astonishment you discover that this is to be taken literally: small heaps of gold are distributed throughout the city. On a certain day, the Phrygians even allow all the tourists to collect as much gold as they can in a limited rectangular area. As it happens, this day is tomorrow, and you decide to become filthy rich on this day. All the other tourists decided the same however, so it’s going to get crowded. Thus, you only have one chance to cross the field. What is the best way to do so?
Given a rectangular map and amounts of gold on every field, determine the maximum amount of gold you can collect when starting in the upper left corner of the map and moving to the adjacent field in the east, south, or south-east in each step, until you end up in the lower right corner.
Input
The input starts with a line containing a single integer, the number of test cases.
Each test case starts with a line, containing the two integers r and c, separated by a space (1 <= r, c <= 1000). This line is followed by r rows, each containing c many integers, separated by a space. These integers tell you how much gold is on each field. The amount of gold never negative.
The maximum amount of gold will always fit in an int.
Output
For each test case, write a line containing “Scenario #i:”, where i is the number of the test case, followed by a line containing the maximum amount of gold you can collect in this test case. Finish each test case with an empty line.
Sample Input
1
3 4
1 10 8 8
0 0 1 8
0 27 0 4
Sample Output
Scenario #1:
42
/*百度翻译:*/
他们说,在佛里吉亚,街道上用黄金铺成的。 你现在在佛里吉亚度假,你惊讶地发现,这是真的:小成堆的黄金的形式分布于整个城市。 甚至在某一天,弗里吉亚允许所有的游客来收集尽可能多的黄金,他们可以在一个有限的矩形区域。 凑巧的是,这一天是明天,你决定在这一天变得非常富有。 然而,所有其他游客决定相同的所以会拥挤。 因此,您只有一个跨领域的机会。 这样做的最好办法是什么?
给定一个矩形图和数量的黄金在各个领域,确定最大数量的黄金时,你可以收集在地图的左上角开始,东邻域,南部,或者东南部在每一步,直到你最终在右下角。
题目大意:
(1)输入第一行两个整数表示下面那个矩阵的行数和列数。
(2)第二行开始输入一个矩阵。
(3)从左上角到右下角,只能向下或者向右走,虽然题目上说的是可以向下、向右、向右下、不过很显然向右下走是多余的。
(4)输出所经过的数字和最大。
这道题由于只能从两个方向我们可以使用DP(DP是dynamic programming的缩写,中文为动态规划编程,是一种编程思想),状态转移方程式就是:
f [ i ] [ j ] = max{ f [ i-1 ] [ j ] , f [ i ] [ j-1 ] };每一步都要求为最优解。。。
那么我们可以从第一排一直递推到最后一排,那么最后一个元素就是我们所求的最大。
本题是按照从上往下走的思路进行,每步都要加上面元素和左边元素中的的最优解。
感悟:我感觉这道题跟数塔问题差不多,只不过这是矩形的。
【1】
//<img alt="大笑" src="http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif" />经过我不懈的努力终于把这道动规题A出来了O(∩_∩)O哈哈~
//这里是从下往上推的
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[1010][1010];
int dp[1010][1010];
int main()
{
int n,m,t,num=1;
scanf("%d",&t);
while (t--)
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++)
for (int j=0;j<m;j++)
scanf("%d",&a[i][j]);
for (int i=n-1;i>=0;i--)
{
for (int j=m-1;j>=0;j--)
{
if (i==n-1&&j==m-1)
dp[i][j]=a[i][j];
else if (i==n-1)
dp[i][j]=dp[i][j+1]+a[i][j];
else if (j==m-1)
dp[i][j]=dp[i+1][j]+a[i][j];
else
dp[i][j]=max(dp[i][j+1],dp[i+1][j])+a[i][j];
}
}
// for (int i=0;i<n;i++)
// {
// for (int j=0;j<m;j++)
// {
// printf("%-d ",dp[i][j]);
// }
// printf("\n");
// }
printf("%d\n",dp[0][0]);
}
}
【2】
用一维数组的dp优化空间复杂度。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[1010][1010];
int dp[1010];
int main()
{
int n,m,t,num=1;
scanf("%d",&t);
while (t--)
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++)
for (int j=0;j<m;j++)
scanf("%d",&a[i][j]);
for (int i=n-1;i>=0;i--)
{
for (int j=m-1;j>=0;j--)
{
if (i==n-1&&j==m-1)
dp[j]=a[i][j];
else if (i==n-1)
dp[j]=dp[j+1]+a[i][j];
else if (j==m-1)
dp[j]=dp[j]+a[i][j];
else
dp[j]=max(dp[j+1],dp[j])+a[i][j];
}
}
// for (int j=m-1;j>=0;j--)
// printf("%-d ",dp[j]);
// printf("\n");
printf("%d\n",dp[0]);
}
}
开始矩阵:
1 10 8 8
0 0 1 8
0 27 0 4过程矩阵:
1 10+z 8+z 8+z
1+s 0+m 1+m 8+m
0+s 27+m 0+m 4+mm代表左边和上边的最大元素
z代表左边元素
s代表上边元素
最终矩阵:
1 11 18 16
1 11 19 27
0 38 38 42
【3】
#include<stdio.h>
#include<string.h>
//~~~~(>_<)~~~~,在这卡了半天......
int a[1010][1010]; //特别注意:如果把二维数组a定义到main函数里,运行就会出错
/*
原因:数组定义在函数中时,占用的内存来自栈空间,栈空间是在进程创建时初始化的,有固定的大小,一般为几十KB,所以太大的数组会耗光栈空间。
而全局变量占用的堆空间,堆空间中的内存是按需分配,自由增长的,可以非常大,32位的系统中可以大到4GB。
*/
int main()
{
int n,m,i,j,k,t,num=1;
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
scanf("%d%d",&n,&m);
scanf("%d",&a[0][0]);
for (i=1;i<m;i++)
{
scanf("%d",&k);
a[0][i]=a[0][i-1]+k; //每列第一行的元素只能由上一列第一行的元素向下走,所以只能加上一列第一行的元素
}
for (i=1;i<n;i++)
{
scanf("%d",&k);
a[i][0]=a[i-1][0]+k; //每行第一列的元素只能由上一行第一列的元素向下走,所以只能加上一行第一列的元素
for (j=1;j<m;j++)
{
scanf("%d",&k);
if (a[i][j-1]>a[i-1][j]) //状态转移过程
a[i][j]=a[i][j-1]+k;
else
a[i][j]=a[i-1][j]+k;
}
}
printf("Scenario #%d:\n",num++);
printf("%d\n",a[n-1][m-1]);
printf("\n");
}
return 0;
}