动态规划 22 07.27
动态规划的定义
- 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。
- 适用条件
任何思想方法都有一定的局限性,超出了特定条件,它就失去了作用。同样,动态规划也并不是万能的。适用动态规划的问题必须满足最优化原理和无后效性。
- 最优化原理(最优子结构性质)
一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质 。 - 无后效性
将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。(满足最优子结构,未来的决策只取决于当前一步,这是动态规划存在的必要条件)
算法实例
- 最典型的背包问题(未记录)
- 蓝肽子序列
- 题目描述
L 星球上的生物由蛋蓝质组成,每一种蛋蓝质由一类称为蓝肽的物资首尾连接成一条长链后折叠而成。
生物学家小乔正在研究 L 星球上的蛋蓝质。她拿到两个蛋蓝质的蓝肽序列,想通过这两条蓝肽序列的共同特点来分析两种蛋蓝质的相似性。
具体的,一个蓝肽可以使用 11 至 55 个英文字母表示,其中第一个字母大写,后面的字母小写。一个蛋蓝质的蓝肽序列可以用蓝肽的表示顺序拼接而成。
在一条蓝肽序列中,如果选取其中的一些位置,把这些位置的蓝肽取出,并按照它们在原序列中的位置摆放,则称为这条蓝肽的一个子序列。蓝肽的子序列不一定在原序列中是连续的,中间可能间隔着一些未被取出的蓝肽。
如果第一条蓝肽序列可以取出一个子序列与第二条蓝肽序列中取出的某个子序列相等,则称为一个公共蓝肽子序列。
给定两条蓝肽序列,找出他们最长的那个公共蓝肽子序列的长度。
#include<iostream>
#include<algorithm>
using namespace std;
string s1,s2;
int cnt1,cnt2;
string str1[1005],str2[1005];
int dp[1005][1005];
int main(){
cin>>s1>>s2;
//将字符拆分放到str的字符数组中
for(int i=0;i<s1.length();){
if(s1[i]>='A'&&s1[i]<='Z'){
str1[++cnt1]+=s1[i++];
while(s1[i]>='a'&&s1[i]<='z'){
str1[cnt1]+=s1[i++];
}
}
}
for(int j=0;j<s2.length();){
if(s2[j]>='A'&&s2[j]<='Z'){
str2[++cnt2]+=s2[j++];
while(s2[j]>='a'&&s2[j]<='z'){
str2[cnt2]+=s2[j++];
}
}
}
//动态转移方程dp[i][j]表示序列的前i个和另一个序列前j个的最大公共序列数
// dp[i][j]=dp[i-1][j-1]+1||dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
for(int i=1;i<=cnt1;i++){
for(int j=1;j<=cnt2;j++){
if(str1[i]==str2[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
} }
cout<<dp[cnt1][cnt2]<<endl;
return 0;
}
//本质为最长公共子序列问题,本题中最小的元为字符串,string类型支持直接比较
//LanQiaoBei
//LanTaiXiaoQiao
//2
- 画廊
小蓝办了一个画展,在一个画廊左右两边陈列了他自己的作品。为了使画展更有意思,小蓝没有等距陈列自己的作品,而是按照更有艺术感的方式陈列。
在画廊的左边陈列了 L 幅作品,在画廊的右边陈列了 R 幅作品。
每周,小蓝要整理一遍自己的每一幅作品。整理一幅作品的时间是固定的,但是要带着沉重的工具。从一幅作品到另一幅作品之间的距离为直线段的长度。
小蓝从画廊的起点的正中央(左右两边的中点)出发,整理好每一幅画,最终到达画廊的终点的正中央。已知画廊的宽为 w。
请问小蓝最少带着工具走多长的距离?
输入描述
输入的第一行包含四个整数 L,R,d,w,表示画廊左边和右边的作品数量,以及画廊的长度和宽度。
第二行包含 L个正整数 ,表示画廊左边的作品的位置。
第三行包含 R个正整数 ,表示画廊右边的作品的位置。
示例
输入
3 3 10 2
1 3 8
2 4 6
输出
14.71
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
double arrl[501],arrr[501];
double dp[501][501][2];
int l,r;
double d,w;
double cal_distance(double x,double y){
return sqrt((x)*(x)+(y)*(y));
}
int main(){
cin>>l>>r>>d>>w;
for(int i=1;i<=l;i++){
cin>>arrl[i];
}
for(int j=1;j<=r;j++)
cin>>arrr[j];
memset(dp,127,sizeof(dp));//127的·目的是使二进制的后八位达到最大的状态,因为memset函数的只取到后面八位。
dp[1][0][0]=cal_distance(w/2,arrl[1]);
dp[0][1][1]=cal_distance(w/2,arrr[1]);
for(int i=0;i<=l;i++){
for(int j=0;j<=r;j++){
//找到对应的状态方程
if(i){
dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+arrl[i]-arrl[i-1],dp[i-1][j][1]+cal_distance(w,arrl[i]-arrr[j])));
}
if(j){
dp[i][j][1]=min(dp[i][j][1],min(dp[i][j-1][0]+cal_distance(w,arrr[j]-arrl[i]),dp[i][j-1][1]+arrr[j]-arrr[j-1]));
}
}
}
double res=min(dp[l][r][0]+cal_distance(w/2,d-arrl[l]),dp[l][r][1]+cal_distance(w/2,d-arrr[r]));
printf("%.2lf\n",res);
return 0;
}
补充总结
- 动态规划算法的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其他的算法。选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上却无法承受,所以我们舍空间而取时间 。
- string类型使用细节
- STL模板,用于可变长字符串
- string同字符串字面值不是一个类型
- str1==str2 可直接判断是否一致,同样也可以判断大小
- str1+str2 为连接两个字符串 注:string对象和字符串字面值混在一起使用,运算符两侧至少出现一个是string
string s4=s1+",";//正确
string s5="hello"+",";//错误,两个都不是string对象
- cin 函数遇到空格就就停止一次读入,string对象也不例外
- memset函数用法,链接: memset的使用注意