前言
我对动态规划的理解:贪心+递推(存储递推结果),即动态更新每一阶段得出的最优解,解决最优化问题。每一个阶段的决策都会使问题的规模和状态发生变化,整个过程满足最优子结构性质。
三个性质:最优化原理,无后效性,子问题重叠。
动态规划的基本思想:把求解的问题分成多个子问题,按顺序求解各子问题,前一子问题的解为后一子问题的求解提供了信息,在求解任一个子问题的时候,列出所有可能的局部解,通过决策(判断)保留那些有可能达到局部最优的局部解,舍弃其他局部解,直到求解到最后一个子问题的解也就是初始问题的解。
由于动态规划有子问题重叠的特点,为了减少重复计算,对每一个子问题只解一次,将其他不同阶段的不同状态保存在一个二维数组中。
解题基本步骤:
- 划分阶段
- 选择状态
- 确定决策并写出状态转移方程(划重点)
接触动态规划的第一道题:ZUEBOJ http://oj.icode8.cn/problem.php?cid=1134&pid=20
在看完动态规划后做这个题有了思路,下面是代码(可能会写的有些麻烦):
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main(){
int n,m,max,t;
int z[103][103],d[103][103];
while(cin >> n >> m){
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin >> z[i][j];
if(i==0||j==0){//边界最大是1
d[i][j]=z[i][j];
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(z[i][j]==1){//如果该位置符合条件
if(i==0){
d[i][j]=1;
}
else if(j==0){
d[i][j]=1;
}
else{//d[i][j]等于左边,左上角,上边的最小值加一
t=min(d[i-1][j],d[i-1][j-1]);
d[i][j]=min(t,d[i][j-1])+1;
}
}
else{
d[i][j]=0;
}
}
}
max=0;
//找到最大值输出
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(max<d[i][j]){
max=d[i][j];
}
// cout << d[i][j] << " ";
}
// cout << endl;
}
cout << max << endl;
}
return 0;
}
接下来是接触动态规划的两道题,让我百思不得其解,最终看洛谷题解写出
AC代码
采药:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main(){
ll t,m;
int z[10005],h[10005],dp[105][1005];
//z[i]代表采取第i株药的时间
//h[i]代表第i株草药的价值
//dp[i][j]代表以j为容量为放入前i个物品(按i从小到大的顺序)的最大价值
while(cin >> t >> m){
for(int i=1;i<=m;i++){
cin >> z[i] >> h[i];
}
for(int i=1;i<=m;i++){
for(int j=t;j>=0;j--){
if(j>=z[i]){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-z[i]]+h[i]);
//所谓的状态转移方程
}
else{
dp[i][j]=dp[i-1][j];
}
}
}
cout << dp[m][t] << endl;
}
return 0;
}
关于采药有一个不理解的地方,这样就是错的
for(int i=1;i<=m;i++){
for(int j=t;j>=z[i];j--){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-z[i]]+h[i]);
}
}
crazy采药(枚举草药,尝试可放入草药的背包 ):
#include<iostream>
#include<algorithm>
//#define int long long
#include<stdio.h>
using namespace std;
typedef long long ll;
const int p=1e4+5,q=1e7+5;
ll z[p],h[p],dp[q];
int main(){
ll t,m;
//z[i]代表采取第i株药的时间
//h[i]代表第i株草药的价值
//dp[t]表示采药时间t能获得的最大价值
while(cin >> t >> m){
//t=v m=n
// while(scanf("%lld%lld",&t,&m)!=EOF){
for(int i=1;i<=m;i++){
// scanf("%lld%lld",&z[i],&h[i]);
cin >> z[i] >> h[i];
}
for(int i=1;i<=m;i++){
for(int j=z[i];j<=t;j++){
dp[j]=max(dp[j],dp[j-z[i]]+h[i]);
/*
最重要的****
状态转移方程
*/
}
}
// printf("%lld\n",dp[t]);
cout << dp[t] << endl;
}
return 0;
}
最后,我对动态规划还是超级灰常极其不理解,以后还是要多做多练,加油!