算法学习笔记七:淘金dp(2)来自中南大学机试

在一片nm的土地上,每一块11的区域里都有一定数量的金子。这一天,你到这里来淘金,然而当地人告诉你,如果你挖了某一区域的金子,上一行,下一行,左边,右边的金子你都不能被允许挖了。那么问题来了:你最多能淘金多少?
输入
对于每组数据,第一行两个数n,m,表示土地的长和宽(1<=n,m<=200)
接下来n行,每行m个数,表示每个区域的金子数量,每个区域的金子数量不超过1000
输出
对于每组数据,输出最多得到的金子数量
样例输入
4 6
11 0 7 5 13 9
78 4 81 6 22 4
1 40 9 34 16 10
11 22 0 33 39 6
样例输出
242

思路:动态规划。
我们先看一个简化版淘金:若只有一行的金子给你挖,然后挖了一个区域的金子则这个区域左边、右边都不允许你挖了,那你最多能挖多少呢?
我们这样思考,设一个数组dp[i]记录到第i点为止可以挖的最多的金子。那么dp[i]的取值一定是取dp[i-1]dp[i-2]+a[i]中的较大值。举个例子吧(设下标从1开始):a[5]={1,2,3,4,5};我们面对a[5]区域有取或者不取2种情况,如果取a[5]区域则a[4]不能取,因此我们可以得到状态方程:dp[5]=max{dp[3]+a[5],dp[4]}所以我们就得出了结论:dp[i]=max{dp[i-2]+a[i],dp[i-1]};有边界条件dp[1]=a[1];dp[2]=max{a[1],a[2]};
而本题淘金思路就是在每一行都用"简化版淘金"的思想求出一个每行的最大值,最后在每列间再使用一次这个思想求出总共的最大值。
代码:

#include
using namespace std;
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		int i,j,a[210][210];
		long long dp[210][210];
		for(i=0;i<n;i++)
			for(j=0;j<m;j++)
			{
				cin>>a[i][j];
			}
		for(i=0;i<n;i++)
		{
			dp[i][0]=a[i][0];
			for(j=1;j<m;j++)
			{
				if(j==1)
					dp[i][j]=a[i][j]>a[i][0]?a[i][j]:a[i][0];
				else
				{
					if(dp[i][j-2]+a[i][j]>dp[i][j-1])//每一行都求出了自己行的最大值
						dp[i][j]=dp[i][j-2]+a[i][j];
					else
						dp[i][j]=dp[i][j-1];
				}
			}
		}
		long long s[210];
		s[0]=dp[0][m-1];//每行的最大值即在最后一列中
		for(i=1;i<n;i++)
		{
			if(i==1)
				s[1]=dp[1][m-1]>dp[0][m-1]?dp[1][m-1]:dp[0][m-1];
			else
			{
				if(s[i-2]+dp[i][m-1]>s[i-1])
					s[i]=s[i-2]+dp[i][m-1];
				else
					s[i]=s[i-1];
			}
		}
		cout<<s[n-1]<<endl;
	 } 
	 return 0;
}

你可能感兴趣的:(算法题)