动态规划专题(找子问题+自下而上)

本文精选了五道动态规划的经典题目进行详细解析,包括滑雪、The Triangle、Common Subsequence、放苹果等,通过实例帮助读者深入理解动态规划的递归思想及应用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态规划算法的核心就是记住已经解决过子问题的解。对问题的求解,其实就是对子问题自底向上的计算过程。
学会动态规划重点是搞清楚递归的作用。
第一步:找到子问题。
P4310 绝世好题
该问题讨论每个数每一位是否为1,最后观察所有数在哪一位上为1的数量最大
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
ll a[nl];
ll f[32];
ll n;
ll i,j;
void dp(){
for(i=0;i<n;i++){
ll mx=0;
for(j=0;j<=30;j++){
if(a[i]&(1<<j)){//(用于判断a[i]的第i位是否为1)
mx=max(mx,f[j]+1);
}
}
for(j=0;j<=30;j++){
if(a[i]&(1<<j)){
f[j]=mx;//求每一位最大值,不准确,但是最后只用输出最大的数,所以无关紧要。
}
}
}
}
int main(){
cin>>n;
for(i=0;i<n;i++){
cin>>a[i];
}
dp();
ll maxn=0;
for(i=0;i<=30;i++){
//cout<<f[i]<<endl;
if(maxn<f[i]){
maxn=f[i];
}
}
cout<<maxn;
}

A - 滑雪
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
1 2 3 4 5

16 17 18 19 6

15 24 25 20 7

14 23 22 21 8

13 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
Sample Output
25
题解:解决这个问题网上两种方法:
1.是利用动态规划的变形
求解从每个点出发的最长路径长度,
求解从每一个出发点最长路径过程就是一个不断求最优解的过程。
关键就是理解动态规划和递归的关系!
代码如下:

#include<bits/stdc++.h> 
using namespace std;
#define ll long long
const ll nl=1e5+5;
ll a[101][101];
ll b[101][101];
ll i,j;
ll n,m;
ll stackl(ll i,ll j){
	if(b[i][j]!=-1){//递归时,会用到,直接返回
		return b[i][j];(局部最优解)
	}
    ll	max=0;
	if(i>0&&a[i][j]>a[i-1][j]&&max<stackl(i-1,j)){//层层递归找到局部最大解
		max=b[i-1][j];
	}
	if(i<n-1&&a[i][j]>a[i+1][j]&&max<stackl(i+1,j)){
		max=b[i+1][j];
	}
	if(j>0&&a[i][j]>a[i][j-1]&&max<stackl(i,j-1)){
		max=b[i][j-1];
	}
	if(j<m-1&&a[i][j]>a[i][j+1]&&max<stackl(i,j+1)){
		max=b[i][j+1];
	}
	b[i][j]=max+1;//别忘记加上自身
	return b[i][j];
}
int main(){
	cin>>n>>m;
	ll i,j;
	for(i=0;i<n;i++){
		for(j=0;j<m;j++){
			cin>>a[i][j];
			b[i][j]=-1;//初始化
		}
	}
    ll max=0;
	for(i=0;i<n;i++){
		for(j=0;j<m;j++){
			b[i][j]=stackl(i,j);
		}
	}
	for(i=0;i<n;i++){
		for(j=0;j<m;j++){
			if(max<b[i][j]){
				max=b[i][j];
			}
		}
	}
	cout<<max<<endl;
}

B - The Triangle
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

(Figure 1)
Figure 1 shows a number triangle. Write a program that calculates the highest sum of numbers passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.
Input
Your program is to read from standard input. The first line contains one integer N: the number of rows in the triangle. The following N lines describe the data of the triangle. The number of rows in the triangle is > 1 but <= 100. The numbers in the triangle, all integers, are between 0 and 99.
Output
Your program is to write to standard output. The highest sum is written as an integer.
Sample Input
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30
题解:
利用动态规划的思想,从后向前依次寻找最合适的数字,每一次寻找都作为当前最大的子问题,并将子问题的答案保留,留作后续操作的选择项。
代码如下:

#include<iostream>
using namespace std;
int a[101][101];
int b[101][101];
int main(){
	int n;
	cin>>n;
	int i,j;
	for(i=0;i<n;i++){
		for(j=0;j<=i;j++){
			cin>>a[i][j];
		}
	}
	for(i=n-2;i>=0;i--){//从后向前寻找。
		for(j=0;j<=i;j++){
			if(a[i+1][j]>a[i+1][j+1]){
				a[i][j]+=a[i+1][j];
			}else{
				a[i][j]+=a[i+1][j+1];
			}
		}
	}
	cout<<a[0][0];
}

D - Common Subsequence
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, …, xm > another sequence Z = < z1, z2, …, zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, …, ik > of indices of X such that for all j = 1,2,…,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.
Input
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.
Output
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.
Sample Input
abcfbc abfcab
programming contest
abcd mnp
Sample Output
4
2
0
题解:
字符串匹配+动态规划(此题递归好像超时);
找子问题:设字符串a,b,二维数组c[][],二维数组c;
在这里插入图片描述
当a[i-1]==b[j-1]时,c[i][j]=c[i-1][j-1]+1;
代码如下:

#include<iostream>
#include<cstring>
using namespace std;
char a[601];
char b[601];
int i,j;
int c[601][601];
int main(){
	while(cin>>a>>b){
		int len1=strlen(a);
		int len2=strlen(b);
		for(i=0;i<=len1;i++){
			for(j=0;j<=len2;j++){
				if(i==0||j==0){
					c[i][j]=0;
				}else{
					if(a[i-1]==b[j-1]){
						c[i][j]=c[i-1][j-1]+1;//自下而上
					}else{
						c[i][j]=max(c[i-1][j],c[i][j-1]);
					}
				}
			}
		}
		cout<<c[len1][len2]<<endl;
	} 
}

E - 放苹果
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
Input
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
Output
对输入的每组数据M和N,用一行输出相应的K。
Sample Input
1
7 3
Sample Output
8

题解:
找子问题:当m<n时,一定有n-m个空盘子,c[m][n]=c[m][m];
当m>=n时,一定有c[m][n]=c[m-n][n]+c[m][n-1];
当m<=1||n==1时c[m][n]=0;
代码如下:

#include<iostream>
using namespace std;
int c[11][11];
int stackl(int a,int b){//递归
	if(a<=1||b==1){
		c[a][b]=1;
	}else{
		if(a<b){
			c[a][b]=stackl(a,a);
		}else{
			c[a][b]=stackl(a-b,b)+stackl(a,b-1);//分为在每个盘子中少拿一块,和少一给盘子,在递归过程中不断减少a和b,使得最终可以有所返回。
		}
	}
	return c[a][b];
}
int main(){
	int t;
	cin>>t;
	while(t--){
	int i,j;
	int m,n;
	cin>>m>>n;
	cout<<stackl(m,n)<<endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值