动态规划(进阶)
进阶失败需要更多经验升级doge
构成动态规划的三要素:
1》状态
2》阶段
3》决策
动态规划求解的三个基本条件
1》子问题重叠性
2》无后效性
3》最优子结构(类似贪心)
dp基础
LIS(最长上升子序列)
状态表示:
F[i]表示a[i]为结尾的“最长上升子序列”长度
阶段划分:
子序列的结尾位置
转移方程:
F[i] = max(F[j] + 1)
边界:
F[0] = 0
LCS(最长公共子序列)
状态表示:
F[i][j]表示前缀子串A[1i],B[1j]的“最长公共子序列”长度
阶段划分:
已经处理的前缀长度
转移方程:
F[i][j] =max(F[i-1,j],F[i,j-1],F[i-1,j-1]+1(if A[i]==B[j]))
边界:
F[i,0] = F[0,j] = 0
区间DP
区间DP也属于线性DP的一种,它以“区间长度”为DP的“阶段”,使用两个坐标描述每个维度。
在区间DP中,一个状态由若干个比它更小且包含于它的区间所代表的状态转移而来,因此区间DP的决策往往就是划分区间的方法。
特点:
合并:即将两个或多个部分进行整合
特征:能将问题分解为能两两合并的形式
求解:对整个问题设最优值,枚举合并点,将问题分解为左右两个部分,最后合并两个部分的最优值得到原问题的最优值
记忆化搜索
按俺的话来讲就是吧dfs和dp搞到一块了
就你得找出来状态转移方程还得写一个dfs然后dp,滑雪就很经典,之前训练的dp里有可以康康。
优缺点
今日A题
1》01背包加强版(可能看不出)
洛谷P1064 [NOIP2006 提高组] 金明的预算方案
读完题我们一般纠结这个主件和附件怎么选,但是可能会忘记,题目里已经说了,附件最多也只有2个,于是我们就有了可乘之机(doge),所以我们只需要dp主件,然后当主件选上的时候我们再加几层判定,如果有一个附件,那么我们就把主件和这一件附件的值dp,两个就两个附件分别和主件dp再两个附件都和主件dp。分析过来就是01背包加了多重判定。
(题目里还说了价格都是10的倍数所以我们dp的时候可以每十个dp一下)
下面上代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int thing_v[33000];//主件体积(就是价格)
int thing_cos[33000];//主件价值(价格乘以重要参数)
int f_thing_v[33000][3];//附件体积,第二个[]里的数表示第几个附件
int f_thing_cos[33000][3];//附件价值
int dp[33000];
int main() {
int money, m;
while (scanf("%d %d", &money, &m) != EOF) {
memset(thing_v, 0, sizeof thing_v);
memset(thing_cos, 0, sizeof thing_cos);
memset(f_thing_v, 0, sizeof f_thing_v);
memset(f_thing_cos, 0 , sizeof f_thing_cos);
memset(dp, 0, sizeof dp);
//以上都是初始化
int cos, imp, mf;
for(int i=1;i<=m;i++){
scanf("%d %d %d", &cos, &imp, &mf);
if(mf==0){//主件存入主件
thing_cos[i] = imp*cos;
thing_v[i] = cos;
}else {//附件编号之后存入附件
f_thing_v[mf][0]++;
f_thing_v[mf][f_thing_v[mf][0]]=cos;
f_thing_cos[mf][f_thing_v[mf][0]]=cos*imp;
}
}
//开始dp
for(int i=1;i<=m;i++){
for(int j=money;j>=thing_v[i];j-=10){//小技巧会快应该不用多说吧
if(thing_v[i]==0)break;//如果是0就是附件位置因为我们dp主件的同时就把附件dp了
dp[j]=max(dp[j], dp[j-thing_v[i]]+thing_cos[i]);//dp主件
if(f_thing_v[i][0]>0) {//当有附件时
if (j >= thing_v[i] + f_thing_v[i][1]){//至少会有一个
dp[j]=max(dp[j], dp[j-thing_v[i] - f_thing_v[i][1]] + thing_cos[i] + f_thing_cos[i][1]);
}
if(f_thing_v[i][0]>1){//有两个附件时
if(j>=thing_v[i]+f_thing_v[i][2]){
dp[j] = max(dp[j], dp[j-(thing_v[i]+f_thing_v[i][2])]+thing_cos[i] + f_thing_cos[i][2]);
}
if(j>=thing_v[i]+f_thing_v[i][1]+f_thing_v[i][2]){
dp[j] = max(dp[j], dp[j-(thing_v[i]+f_thing_v[i][2]+f_thing_v[i][1])]+thing_cos[i] + f_thing_cos[i][2]+ f_thing_cos[i][1]);
}
}
}
}
}
printf("%d\n", dp[money]);
}
return 0;
}
2》多重背包(还是完全背包?傻傻分不清楚)
读题之后,理解一下就是,价值是摆放种类数,背包空间为放的个数,然后有几组,每组有若干个,因为一组的必须放在一起所以可以,用三层循环直接背包
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int a[111];
int dp[111]={0};
int main(){
int n, m;
scanf("%d %d", &n, &m);
for(int i=1;i<=n;i++){
scanf("%d", &a[i]);
}
dp[0]=1;
for(int i=1;i<=n;i++){
for(int j=m;j>0;j--){
for(int k=1;k<=a[i];k++){
if(j>=k)dp[j]=(dp[j-k]+dp[j])%1000007;
else break;
}
}
}
printf("%d\n", dp[m]);
}