目前只有数字三角形模型,其余待更新
总说
线性DP,是指在线性结构上进行状态转移,是一个线性的递推关系。可以是一维线性的、二维线性的、三维线性的。
以下总结一些常见模板模型, 还有很多题需要现场推导,
一、数字三角形模型
1.1 数字三角形(模板题)
题目描述:
思路
对于图上的每一点,只有2种更新方式,从左上或者右上来,我们存下最大值就行。
代码模板:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510;
const int INF = 0x3f3f3f3f;
int n;
int w[N][N], f[N][N];
int main()
{
scanf("%d", &n);
memset(f, -0x3f, sizeof f); //初始化
for(int i = 1; i <= n; i++) //读入数据
for(int j = 1; j <= i; j++)
scanf("%d",&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 - 1] + w[i][j], f[i - 1][j] + w[i][j]);//取左上、右上最大值 + 当前位置的值
int res = -INF;
for(int i = 1; i <= n; i++)
res = max(res, f[n][i]); //在最后一层找最大值
printf("%d", res);
return 0;
}
1.2 摘花生
题目描述:
题目链接:1015. 摘花生 - AcWing题库
思路
代码模板:
这一题与1.1思路完全相同,更新方式也一样,我们直接给出代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 110;
int w[N][N], f[N][N];
int t, r, c;
int main()
{
scanf("%d", &t);
while(t--)
{
memset(f, 0, sizeof f); //每次初始化dp数组
scanf("%d %d", &r, &c);
for(int i = 1; i <= r; i++) //读入数据,从1,1开始是因为从0,0开始的话可能会越界
for(int j = 1; j <= c; j++)
scanf("%d", &w[i][j]);
for(int i = 1; i <= r; i++)
for(int j = 1; j <= c; j++)
f[i][j] = max(f[i][j - 1] + w[i][j], f[i - 1][j] + w[i][j]);//只能从上或左 来到这个位置
printf("%d\n", f[r][c]);
}
return 0;
}
1.3 最低通行费
题目描述
思路
商人要从左上角走到右下角,第一眼看见商人能上下左右行走,有点像搜索题,但是题目要求商人必须在(2N - 1)个单位实时间出去,也就说明他只能往右 或者 往下,否则就会超时。
代码模板
#include<iostream>
#include<cstring>
using namespace std;
const int N = 110;
int w[N][N], f[N][N];
int n, m;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%d", &w[i][j]);
memset(f, 0x3f, sizeof f); //初始化
f[1][1] = w[1][1]; //初始化起点
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
f[i][j] = min(min(f[i][j], f[i - 1][j] + w[i][j]), f[i][j - 1] + w[i][j]);
printf("%d",f[n][n]);
return 0;
}
优化
由于我们计算 f[i] 层时,只用到了 f[i-1] 层的数据
所以我们可以优化成一维
由于不会像01背包一样被覆盖,所以也不用从大到小枚举。
优化后代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 110;
int w[N][N], f[N];
int n;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%d", &w[i][j]);
memset(f, 0x3f, sizeof f); //初始化
f[1] = 0; //初始化起点
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
f[j] = min(f[j] + w[i][j], f[j - 1] + w[i][j]);
printf("%d", f[n]);
return 0;
}
1.4 方格取数
题目描述
思路
代码模板
二、最长上升子序列
2.1