终于开始写人生第一道DP了。
第一题,耳熟能详的猴子吃香蕉问题。
题目的话,就不放出来了,去lightoj上就能找到。
在一个菱形中找到一条路径,使得猴子能吃到的香蕉数最多。
看下图,题目比较容易想到,用一个相同的表记录走到某一个为止能吃到的香蕉数,每次决策取最大的就是了。
实际编程时,我们是以二维数组来填表的,所以请把下图想象成一个实心右箭头’>’样子的三角形
7 0 0 0
6 4 0 0
2 5 10 0
9 8 12 2
2 12 7 0
8 2 0 0
10 0 0 0
我们定义
input[i][j]:第i行第j列的格子所拥有的香蕉数,
dp[i][j]:走到第i行第j列的格子可以吃到的最大香蕉总数。
菱形的行数:2*N-1
最终dp[2*N-2][0],保存的值就是答案
则从头往下递推可以得到下面的状态转移方程
dp[i][j]=⎧⎩⎨⎪⎪⎪⎪⎪⎪input[i][j]①input[i][j]+dp[i−1][j]②input[i][j]+max(dp[i−1][j−1],dp[i−1][j])③input[i][j]+max(dp[i−1][j],dp[i−1][j+1])④
①: j=i=0
②: j=0,0<i<N
③: 0<j<i,0<i<N
④: i>=N
上面的状态有点多,如果我们换一种方式
0 7 0 0 0
0 6 4 0 0
0 2 5 10 0
0 9 8 12 2
0 2 12 7 0
0 8 2 0 0
0 10 0 0 0
则②的转移可以合并成③
以下是第一种状态转移的代码,代码规范就请忽略吧。第二种状态转移的实现就不写了,网上也能找到别的大神的实现。虽然AC了,但待优化的地方还有很多。
#include <iostream>
#include <cstdio>
#include <math.h>
#include<memory.h>
using namespace std;
int main() {
int N, caseno = 0, cases;
scanf("%d", &cases);
while( cases-- ) {
cin>>N;
long long input[2*N-1][N];//输入
long long dp[2*N-1][N];
memset(input,0,sizeof(input));
memset(dp,0,sizeof(dp));
for(int i=0;i<2*N-1;i++)
{
if(i<N)
{
for(int j=0;j<=i;j++)
{
cin>>input[i][j];
}
}
else
{
for(int j=0;j<2*N-i-1;j++)
{
cin>>input[i][j];
}
}
}
/* for(int i=0;i<2*N-1;i++)//测试输出
{
for(int j=0;j<N;j++)
{
cout<<input[i][j]<<" ";
}
cout<<endl;
}
*/
//DP递推
dp[0][0] = input[0][0];//初始条件
dp[0][2] = 0;
for(int i=1;i<2*N-1;i++)
{
if(i<N)
{
for(int j=0;j<=i;j++)
{
if(j==0)
{
dp[i][j]=input[i][j]+dp[i-1][j];
}
else
{
dp[i][j] = input[i][j] + max(dp[i-1][j-1],dp[i-1][j]);
}
}
}
else
{
for(int j=0;j<2*N-i-1;j++)
{
dp[i][j]=input[i][j] + max(dp[i-1][j],dp[i-1][j+1]);
}
}
}
/* for(int i=0;i<2*N-1;i++)//测试输出
{
for(int j=0;j<N;j++)
{
cout<<dp[i][j]<<" ";
}
cout<<endl;
}*/
cout<<"Case "<<++caseno<<": "<<dp[2*N-2][0]<<endl;
}
return 0;
}