第一:先将题目化简
第二:建立数组dp[i][j],给dp[i][j]赋予意义
第三:计算出dp[0][0],dp[0][1],dp[0][2],dp[1][0],dp[1][1],dp[1][2],dp[2][0],dp[2][1],dp[2][2]观察他们之间的关系变化,观察每次增加数据量的变化
第四:将最容易得到的dp[i][j]进行赋值
第五:用双重循环给dp[i][j]进行赋值
这里拿出一道例题进行对应分析:
试题 算法训练 拿金币
问题描述
有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。
输入格式
第一行输入一个正整数n。
以下n行描述该方格。金币数保证是不超过1000的正整数。
输出格式
最多能拿金币数量。
样例输入
3
1 3 3
2 2 2
3 1 2
样例输出
11
数据规模和约定
n<=1000
第一步将题目简化:
将题目简化为:
2
1 3
2 2
第二步:建立数组dp[i][j],给dp[i][j]赋予意义:
建立一个dp[i][j],根据题目为其赋予意义
这里dp[i][j]被赋予的意义是,在第i行第j列最多能拿金币数量
第三步:计算出dp[0][0],dp[0][1],dp[0][2],dp[1][0],dp[1][1],dp[1][2],dp[2][0],dp[2][1],dp[2][2]观察他们之间的关系变化,观察每次增加数据量的变化:
dp[1][1]=1 ,dp[1][2]=1+3
dp[2][1]=1+2,dp[2][2]=3+2
很容易观察到dp[2][2]=max(dp[2-1][2],dp[2][2-1])+第i行第i列格子里面的金币
很容易可以将dp[2][2]推广到dp[i][j]
即:
dp[i][j]=max(dp[i-1][j],dp[i][j-1]+第i行第i列格子里面的金币
第四:将最容易得到的dp[i][j]进行赋值:
很容易得出:dp[0][j]=0,dp[i][0]=0;
检验一下:dp[1][1]=max(dp[0][1],dp[1][0])+第1行第1列格子里面的金币=0+1=1
dp[1][2]=max(dp[0][2],dp[1][1])+第1行第2列格子里面的金币=1+3=4
dp[2][1]=max(dp[1][1],dp[2][0])+第2行第1列格子里面的金币=1+2=3
dp[2][2]=max(dp[1][2],dp[2][1]+第2行第2列格子里面的金币
=max(4,3)+2=6
满足实际
第五:用双重循环给dp[i][j]进行赋值:
再进行给dp[i][j]赋值之前,还有 “第i行第j列格子里面的金币” 这个数据没有表示出来
用数组a[i][j]表示 第i行第j列格子里面的金币
即:
for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ scanf("%d",&x); a[i][j]=x; } }
再给dp[i][j]进行赋值:
for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ dp[i][j]=max(dp[i-1][j]+dp[i][j-1])+a[i][j]; } }
这里附上完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int a[1001][1001];
int dp[1001][1001];
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int max(int a,int b){
return a>b?a:b;
}
int main(int argc, char *argv[]) {
int i,j,n,x;
scanf("%d",&n);
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
scanf("%d",&x);
a[i][j]=x;
}
}
for(i=0;i<=n;i++){
dp[0][i]=0;
dp[i][0]=0;
}
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];
}
}
printf("%d",dp[n][n]);
return 0;
}