动态规划详细解释

推荐博客:https://www.zhihu.com/question/23995189/answer/613096905

动态规划满足的条件】

最优子结构性质:大问题的最优解可以由小问题的最优解推出,这个性质叫做“最优子结构性质”。

无后效性:一旦f(n)确定,“我们如何凑出f(n)”就再也用不着了。

要求出f(15),只需要知道f(14),f(10),f(4)的值,而f(14),f(10),f(4)是如何算出来的,对之后的问题没有影响。

“未来与过去无关”,这就是无后效性。(严格定义:如果给定某一阶段的状态,则在这一阶段以后过程的发展不受这阶段以前各段状态的影响。)

 

【如何设计DP算法】

  下面介绍比较通用的设计DP算法的步骤。

  首先,把我们面对的局面表示为x。这一步称为设计状态。对于状态x,记我们要求出的答案(e.g. 最小费用)为f(x).我们的目标是求出f(T).   

       找出f(x)与哪些局面有关(记为p),写出一个式子(称为状态转移方程),通过f(p)来推出f(x).

 

【DP三连】

  设计DP算法,往往可以遵循DP三连:

  我是谁? ——设计状态,表示局面
  我从哪里来?
  我要到哪里去? ——设计转移

 设计状态是DP的基础。接下来的设计转移,有两种方式:一种是考虑我从哪里来(本文之前提到的两个例子,都是在考虑“我从哪里来”);另一种是考虑我到哪里去,这常见于求出f(x)之后,更新能从x走到的一些解。这种DP也是不少的,我们以后会遇到。

  总而言之,“我从哪里来”和“我要到哪里去”只需要考虑清楚其中一个,就能设计出状态转移方程,从而写代码求解问题。前者又称pull型的转移,后者又称push型的转移。

 
例题讲解:换硬币
 
如果一个奇葩国家的钞票面额分别是1、5、11,那么我们在凑出w的时候钞票的最小张数。

[公式] 只与 [公式] 相关;更确切地说:

[公式]

pull型的转移方程:我从哪里来

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 //我要到哪里去”---push型的转移方程
 6 int f[105],i,n,cost;
 7 int main()
 8 {
 9     scanf("%d",&n);
10     f[0]=0;
11     f[1]=1;
12     for(i=0;i<=105;i++)
13     f[i]=100000;
14     f[0]=0;
15     for(i=1;i<=n;i++)
16     {
17         int cost=100000;
18         if(i>=1)  cost=min(cost,f[i-1]+1);
19         if(i>=5)  cost=min(cost,f[i-5]+1);
20         if(i>=11)  cost=min(cost,f[i-11]+1);
21         f[i]=cost;
22     }
23     for(i=1;i<=n;i++)
24         cout<<f[i]<<" ";
25 }

 

例题二:最长子序列

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 //求数组递增的最长子序列
 6 int f[105],i,n,cost;
 7 int main()
 8 {
 9     int a[105];
10     scanf("%d",&n);
11     for(int i=1;i<=n;i++)
12     {
13         scanf("%d",&a[i]);
14         f[i]=1;
15     }
16 
17     for(int x=1;x<=n;x++)
18     {
19         for(int p=1;p<x;p++)
20             if(a[p]<a[x])
21             f[x]=max(f[x],f[p]+1);
22         cout<<f[x]<<endl;
23     }
24     int ans=0;
25     for(int x=1;x<=n;x++)
26        ans=max(ans,f[x]);
27     cout<<"max:"<<ans<<endl;
28 }

 

 

 

push型的转移方程:我要到哪里去

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<queue>
 6 #define inf 0x3f3f3f3f
 7 using namespace std;
 8 //我要到哪里去”---push型的转移方程
 9 int f[105],i,n,cost;
10 int main()
11 {
12     scanf("%d",&n);
13     f[0]=0;
14     f[1]=1;
15    // memset(f,inf,sizeof(f));
16    for(i=0;i<=105;i++)
17     f[i]=100000;
18      f[0]=0;
19     f[1]=1;
20     for(i=0;i<=n;i++)
21     {
22         f[i+1]=min(f[i+1],f[i]+1);
23         f[i+5]=min(f[i+5],f[i]+1);
24         f[i+11]=min(f[i+11],f[i]+1);
25     }
26     for(i=1;i<=n;i++)
27         cout<<f[i]<<" ";
28 }

 

 

 

 
 
 
 
 
 

转载于:https://www.cnblogs.com/Aiahtwo/p/11305320.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值