十三、常见动态规划问题
13.1 从分治法到动态规划
动态规划:
1、设计一个数据结构,记录不同规模问题的答案
2、数据结构采用从小到大的方式去生成
一般流程:
1、大问题分解 -> 小问题
2、解决最小问题
3、记录最小问题,从最小问题出发回推大问题
跳台阶
先用分治法去思考问题(从大问题到小问题),再反向思考
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
int dp[16] = { 0 };
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
printf("%d\n", dp[n]);
return 0;
}
13.2 放苹果
动态规划和排列组合问题
二维数组
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string>
using namespace std;
int main() {
int dp[13][13] = { 0 };
int n, m; //dp[m][n] 将n个苹果放入m个盘子中
while (scanf("%d%d", &m, &n) != EOF) {
memset(dp, 0, 13 * 13);
for (int i = 0; i <= m; i++) {
dp[i][1] = 1;
}
for (int j = 1; j <= n; j++) {
dp[0][j] = 1;
dp[1][j] = 1;
}
for (int i = 2; i <= m; i++) {
for (int j = 2; j <= n; j++) {
if (j > i) {
dp[i][j] = dp[i][i];
}
else {
dp[i][j] = dp[i][j - 1] + dp[i - j][j];
}
}
}
printf("%d\n", dp[m][n]);
}
}
13.3 最长公共子序列
和线性数据结构相关的动态规划问题
两个序列 求最长公共子序列 最长公共连续子序列
一个序列内 求最大子序列和
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string>
#include<algorithm>
using namespace std;
int dp[1001][1001]; //dp[i][j] s1的前i个元素 s2的前j个元素的最大公共长度
int main() {
int m, n;
char s1[1001], s2[1001];
scanf("%d%d", &n, &m);
scanf("%s%s", s1, s2);
memset(dp, 0, 1002 * 1002);
for (int i = 0; i <= n; i++) {
dp[i][0] = 0;
}
for (int j = 0; j <= m; j++) {
dp[0][j] = 0;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s1[i-1] == s2[j-1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
printf("%d\n", dp[n][m]);
return 0;
}
13.4 最长公共子串
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string>
#include<algorithm>
using namespace std;
short dp[10001][10001] = { 0 };
int main() {
int n, m;
char s1[10001];
char s2[10001];
scanf("%s%s", s1, s2);
n = strlen(s1);
m = strlen(s2);
short curmax = 0;
for (int i = 0; i <= n; i++) {
dp[i][0] = 0;
}
for (int j = 0; j <= m; j++) {
dp[0][j] = 0;
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (s1[i-1] >= 'a' && s1[i-1] <= 'z' && s1[i-1] == s2[j-1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
curmax = max(dp[i][j], curmax);
}
else {
dp[i][j] = 0;
}
}
}
printf("%d\n", curmax);
return 0;
}
13.5 最大序列和
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<string>
long long s[1000001]; //-10^9 -10^9
long long dp[1000002]; //dp[i]前i个元素 必须包含右边缘的最大子序列和
using namespace std;
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%lld", &s[i]);
}
dp[1] = s[0];
long long curmax = dp[1];
for (int i = 2; i <= n; i++) {
if (dp[i - 1] < 0) {
dp[i] = s[i - 1];
}
else {
dp[i] = s[i - 1] + dp[i - 1];
}
curmax = max(curmax, dp[i]);
}
printf("%lld\n", curmax);
return 0;
}
13.6 背包问题
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<algorithm>
using namespace std;
int main(){
int c,n;
scanf("%d%d",&c,&n);
int p[101] = {0};
int v[101] = {0};
for(int i = 0; i< n;i++){
scanf("%d%d",&p[i],&v[i]);
}
int dp[1001][101];
//dp[x][y] 在总额度不超过x的情况,考察0-y-1号商品的最大评分
for(int x=0;x<=c;x++){
dp[x][0] = 0;
}
for(int y=0; y<=n;y++){
dp[0][y] = 0;
}
for(int x=1;x<=c;x++){
for(int y=1;y<=n;y++){
if(x-p[y-1]<0){
dp[x][y] = dp[x][y-1];
}else{
dp[x][y] = max(dp[x][y-1],dp[x-p[y-1]][y-1]+v[y-1]);
}
}
}
printf("%d\n",dp[c][n]);
return 0;
}