给出了几种创建的动态规划题目适合笔试的解法,包含状态转移方程、状态转移矩阵(例子)和核心代码。子序列问题是需要复制元素在矩阵上侧或者左侧的值,而子串问题则不需要
目录
1 强盗抢劫问题
问题:给定一个数组,表示一条街上每个房间的金钱数。盗贼抢劫这条街时,如果抢劫了相邻的两个房间,则会触发报警。问盗贼最多能抢到多少钱?
思路:就是看抢当前这个还是后一个哪个好
核心代码:
int rob(int Array[], int low, int high){
int pre2=0, pre1=0;
for(int i=low; i<=high; i++){
int cur = max(pre2+Array[i], pre1);
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
问题升级:第一个房子和最后一个房子成了邻居,所以我们就不可以同时把他们两家一块儿抢了
思路:1、如果打劫第一个房间,那么最后一个房间不可以打劫,只计算到n-1;
2、如果不打劫第一个房间,那么可以考虑最后一个房间,从第2个房间开始计算,到第n个房间。然后再取两者的最大值
int max = max(rob(Array, 0, n-2), rob(Array, 1, n-1));
2 最大公共子序列
输入:"1A2C3D4B56","B1D23CA45B6A"
输出:6 (因为最大子序列是“123456”)
状态转移方程:
if(DP[i][j] == DP[i-1][j-1]): DP[i][j] = DP[i-1][j-1]+1
else: DP[i][j] = Max(DP[i-1][j], DP[i-1][j])
状态转移矩阵:
B | 1 | D | 2 | 3 | C | A | 4 | 5 | B | 6 | A | ||
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
A | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
2 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 |
C | 0 | 0 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
3 | 0 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
D | 0 | 0 | 1 | 1 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
4 | 0 | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
B | 0 | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 4 | 4 | 4 | 4 | 4 |
5 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 3 | 4 | 4 | 5 | 5 | 5 |
6 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 3 | 4 | 5 | 5 | 5 | 5 |
0 | 1 | 1 | 2 | 2 | 3 | 3 | 3 | 4 | 5 | 5 | 6 | 6 |
代码:
int main() {
char str1[100],str2[100];
printf("Input the String 1:");
gets(str1);
printf("Input the String 2:");
gets(str2);
int len1 = strlen(str1);
int len2 = strlen(str2);
int dp[len1+1][len2+1];
memset(dp, 0, sizeof(dp));
for(int i=1; i<len1+1; i++) {
for(int j=1; j<len2+1; j++) {
if(str1[i-1] == str2[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 ", dp[i][j]);
}
printf("\n");
}
printf("\n\n%d", dp[len1][len2]);
return 0;
}
3 最大公共子串
输入:"1AB2345CD","12345EF"
输出:"2345"
状态转移方程:
if(DP[i][j] == DP[i-1][j-1]): DP[i][j] = DP[i-1][j-1]+1
状态转移矩阵:
1 | 2 | 3 | 4 | 5 | E | F | ||
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
A | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
B | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 |
C | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0 |
D | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
代码:
int len1 = strlen(str1);
int len2 = strlen(str2);
int dp[len1+1][len2+1];
memset(dp, 0, sizeof(dp));
int max=0, end=0;
for(int i=1; i<len1+1; i++)
for(int j=1; j<len2+1; j++)
if(str1[i-1]==str2[j-1]) {
dp[i][j] = dp[i-1][j-1]+1;
if(max<dp[i][j]){
max = dp[i][j];
end = i;
}
}
if(max==0)
printf("-1");
else {
for(int i=end-max; i<end; i++)
printf("%c", str1[i]);
}
4 最大上升子序列
输入:2 1 5 3 6 4 8 9 7
输出:4
状态转移方程: if( Arr[i]>Arr[j] && Longest[i]<Longest[j]+1 ) Longest[i] = Longest[j]+1
状态转移矩阵:
2 | 1 | 5 | 3 | 6 | 4 | 8 | 9 | 7 |
1 | 1 | |||||||
1 | 1 | 2 | ||||||
1 | 1 | 2 | 2 | |||||
1 | 1 | 2 | 2 | 3 | ||||
1 | 1 | 2 | 2 | 3 | 3 | |||
1 | 1 | 2 | 2 | 3 | 3 | 4 | ||
1 | 1 | 2 | 2 | 3 | 3 | 4 | 5 | |
1 | 1 | 2 | 2 | 3 | 3 | 4 | 5 | 4 |
代码:
int getMax(int Array[], int n){
int longest[n], max=0;
longest[0] = 1;
for(int i=1; i<n; i++) {
longest[i] =1;
for(int j=0; j<i; j++){
if(Array[j] < Array[i] && longest[i] < longest[j]+1) {
longest[i] = longest[j]+1;
if(longest[i]>max)
max = longest[i];
}
}
for(int k=0; k<n; k++)
printf("%d ", longest[k]);
printf("\n");
}
return max;
}
5 三角形最大路径长度
输入:
5∥三角形行数。下面是三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出:30
状态转移方程:DP[i][j] = Max(DP[i+1][j], DP[i+1][j+1])
状态转移矩阵:
7 | => | 30 | ||||||||
3 | 8 | 23 | 21 | |||||||
8 | 1 | 0 | 20 | 13 | 10 | |||||
2 | 7 | 4 | 4 | 7 | 12 | 10 | 10 | |||
4 | 5 | 2 | 6 | 5 | 4 | 5 | 2 | 6 | 5 |
代码:
for(int i = n-2; i >= 0; --i)
for(int j=0; j <= i; j++)
Array[i][j] += max(Array[i+1][j], Array[i+1][j+1]);
6 背包问题
问题:一个旅行者有一个最多能装m公斤的背包,现在有n中物品,每件的重量分别是W1、W2、……、Wn,每件物品的价值分别为C1、C2、……、Cn,需要将物品放入背包中,要怎么样放才能保证背包中物品的总价值最大?
输入:
5 100 //n capacity
20 30
30 10
65 100
10 30
70 35
状态转移方程:
if(weight[i]<j): DP[i][j] = Max(DP[i-1][j], DP[i-1][j-weight[i]]+value[i])
else: DP[i][j] = DP[i-1][j]
状态转移矩阵:
5 | 100 | 1 | ... | 10 | ... | 20 | ... | 30 | ... | 40 | ... | 50 | ... | 65 | ... | 70 | ... | 75 | ... | 80 | ... | 85 | ... | 95 | ... | 100 | |||||
weight | value | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||||
20 | 30 | 20 | 0 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | |||||||
30 | 10 | 30 | 0 | 30 | 30 | 30 | 30 | 30 | 30 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | 40 | |||||||
65 | 100 | 65 | 0 | 30 | 30 | 30 | 30 | 30 | 30 | 40 | 40 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 100 | 130 | 130 | 130 | 130 | 130 | |||||||
10 | 30 | 10 | 0 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 40 | 40 | 100 | 100 | 100 | 100 | 130 | 130 | 130 | 130 | 130 | 130 | 160 | 160 | 160 | |||||
70 | 35 | 70 | 0 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 30 | 40 | 40 | 100 | 100 | 100 | 100 | 130 | 130 | 130 | 130 | 130 | 130 | 160 | 160 | 160 |
代码:
#include<stdio.h>
#define V 1500
int dp[10][V];//全局变量,自动初始化为0
int weight[10];
int value[10];
#define max(x,y) (x)>(y)?(x):(y)
int main()
{
//状态转移方程 dp[i+1][j]=max{dp[i][j],dp[i][j-weight[i+1]+value[i+1]}
int n,capacity;
scanf("%d %d",&n,&capacity);//N物品个数 M背包容量
for (int i=1;i<=n; i++)
scanf("%d %d",&weight[i],&value[i]);
//动态规划
for (int i=1; i<=n; i++)
for (int j=1; j<=capacity; j++)
if (weight[i]<=j)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
else
dp[i][j]=dp[i-1][j]; //实际目的是为了防止地址越界
printf("%d\n",dp[n][capacity]);//输出最优解
}