目录
数字三角形
题目链接
状态表示:来自左上f[i][j] = f[i-1][j-1]
来自右上f[i][j] = f[i-1][j]
f[i][j]=max(f[i-1][j],f[i-1][j-1])+w[i][j]
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=510,INF=0x3f3f3f3f;
int f[N][N],w[N][N];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=0;j<=i+1;j++)
f[i][j]=-INF;//结果可以有负数,所以要将两边变为-INF;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
cin>>w[i][j];
f[1][1]=w[1][1];
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
{
f[i][j]=max(f[i-1][j],f[i-1][j-1])+w[i][j];
}
int ret=-INF;
for(int i=1;i<=n;i++)//找出第n行的最大值
ret=max(ret,f[n][i]);
cout<<ret<<endl;
return 0;
}
可以从下往上dp优化
状态表示:f[i][j] = max ( f[i+1][j] , f[i+1][j+1] ) + w[i][j];
此时不需要初始化f[i][j]
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int f[N][N],w[N][N];
int n;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
cin>>w[i][j];
for(int i=n;i>=1;i--)
for(int j=1;j<=i;j++)
f[i][j]=max(f[i+1][j],f[i+1][j+1])+w[i][j];
cout<<f[1][1]<<endl;
return 0;
}
摘花生
题目链接
状态转移
(i, j)从(i-1, j)即上方过来
(i, j)从(i, j-1)即左方过来
状态表示:f[i][j] = max ( f[i-1][j] , f[i][j-1] ) + w[i][j];
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int w[N][N],f[N][N];
int c,n,m;
int main()
{
cin>>c;
while(c--){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>w[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]=max(f[i-1][j],f[i][j-1])+w[i][j];
cout<<f[n][m]<<endl;
}
return 0;
}
空间优化
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[j]=max(f[j],f[j-1])+w[i][j];
cout<<f[m]<<endl;
memset(f, 0, sizeof f);//每次结束都得初始化
注:每次结束必须对f[M]初始化
由于多组样例,而二维数组解法由于f[0][...]和f[...][0]都为0,所以没有问题。对于一维数组,上一样例的f数组需要清零,否则影响结果
优化后AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int w[N][N],f[N];
int c,n,m;
int main()
{
cin>>c;
while(c--){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>w[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[j]=max(f[j],f[j-1])+w[i][j];
}
cout<<f[m]<<endl;
memset(f, 0, sizeof f);//每次都得初始化
}
return 0;
}
传纸条
题目链接
解题思路:可以看做两人同时由起点出发,同时迈出每一步可右可下,走两条不相交的路
状态表示: f[k][i][j] = max ( f[k][i][j] , f[k - 1][i - a][j - b] + t );
k表示走到第k步,i表示第一个人在前k步中向右走的步数(即第一个人所在位置的横坐标)
j表示第二个人在前k步中向右走的步数(即第二个人所在位置的横坐标)
t表示在第k步是产生的总价值
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 55;
int n, m;
int g[N][N];
int f[N * 2][N][N];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &g[i][j]);
for (int k = 2; k <= n + m; k ++ )//k表示走到第k步;
for (int i = max(1, k - m); i <= n && i < k; i ++ )
//i表示第一个人在前k步中向右走的步数(即第一个人所在位置的横坐标)
for (int j = max(1, k - m); j <= n && j < k; j ++ )
//j表示第二个人在前k步中向右走的步数(即第二个人所在位置的横坐标)
for (int a = 0; a <= 1; a ++ )
for (int b = 0; b <= 1; b ++ )
//上面两层循环为两人第k步是向右还是向下的情况
//一共4种(一右二下,一右二右,一下二下,一下二右)
{
int t = g[i][k - i];//第k步时第一个人产生的价值
if (i != j || k == 2 || k == n + m)
//k==2表示起点(1,1) k==n+m表示终点(n,m)
{
t += g[j][k - j];//第k步的总价值
f[k][i][j] = max(f[k][i][j], f[k - 1][i - a][j - b] + t);
//更新两人分别走了k步产生的最大总价值
}
}
printf("%d\n", f[n + m][n][n]);
return 0;
}