您的位置:首页 > 其它

算法学习笔记4-动态规划-背包问题

2017-09-11 18:13 141 查看

动态规划(Dynamic Programming)

其实动态规划特别抽象,特别难讲。这里也可以参考一篇博文,用讲故事的方式讲了出来。

通过金矿模型介绍动态规划

我的理解就是“用更多的问题来回答问题”。

动态规划是一种求解最优化问题的方法,因为它没有一个确定的数学表达式,或者是明确的解题步骤,说起来会比较抽象,我们只能在实际题目中体会。首先说几个概念吧:

这里先给个栗子:

给定一个整数序列,求这个序列的最长上升子序列,子序列可以是不连续的。比如对于序列(1,7,2,8,3,4),它的子序列是(1,2,3,4)。

1. 阶段

动态规划含有一种递推的思想,如果一个问题可以分成不同的“阶段”,上一个阶段的决策直接影响下一个阶段,我们就可以考虑使用动态规划,依次求出每一个阶段的最优解,直到解决整个问题,也就是拆分问题。

比如上面的例子,我们可以把问题拆解为求以k结尾的序列的最长子序列,也就是分别求序列{[1],[1,7],[1,7,2],[1,7,2,8],[1,7,2,8,3],[1,7,2,8,3,4]}的最长上升子序列。

2. 状态

每一个阶段的客观条件我们称为“状态”。一个阶段可能有一个或多个状态,我们可以用一个状态变量来表示,比如上面每一个阶段可以表示为{[1],[1,7],[1,7,2],[1,7,2,8],[1,7,2,8,3],[1,7,2,8,3,4]}。

这里我们可以看到,其实阶段和状态有时候是不区分的。

3. 决策(状态转移方程)

从一个阶段演变到下一个阶段,叫做“决策”,也就是说不同的决策可能会演变到不同的阶段。

而状态跟状态之间的关系式,叫“状态转移方程”,其实它影响了我们的决策,比如上面的例子,在选择数字1之后,下一步是选择7还是选择2,就需要决策,而如果我们的状态转移方程是F_i=min(A_i,A_i-1),那么我们就会选择较小的2。

这样,每个阶段作出决策,我们就能得到最终问题的解。

上面例子的具体代码实现如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int n;
vector<int>v;
vector<int>::iterator it;
cin>>n;
int *a=new int
;
int *dp=new int
{1};
for(int i=0;i<n;i++)
cout<<dp[i]<<" ";
cout<<endl;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=1;i<n;i++)
{
for(int j=0;j<i;j++)
{
if(a[j]<a[i])
dp[i]=max(dp[i],dp[j]+1);
}
}
cout<<dp[n-1]<<endl;
for(int i=0;i<n;i++)
cout<<dp[i]<<" ";
cout<<endl;
delete []a;
delete []dp;
return 0;
}


下面给出几个实际问题:

1.背包问题

题目描述:

辰辰是个很有潜能、天资聪颖的孩子,他的梦想是称为世界上最伟大的医师。 为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。 医师把他带到个到处都是草药的山洞里对他说: “孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。 我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?

输入描述:

输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),T代表总共能够用来采药的时间,M代表山洞里的草药的数目。

接下来的M行每行包括两个在1到100之间(包括1和100)的的整数,分别表示采摘某株草药的时间和这株草药的价值。

#coding = utf-8
#backpack problem/dynamic problem
while True:
try:
time,m=raw_input().split()
time=int(time)
m=int(m)
t=[]#time
v=[]#value
for i in xrange(m):
t1,t2=raw_input().split()
t1=int(t1)
t2=int(t2)
t.append(t1)
v.append(t2)
dp=[0 for i in range(time+1)]
for i in xrange(m):
for j in xrange(time,t[i]-1,-1):
dp[j]=max(dp[j],dp[j-t[i]]+v[i])
print dp[time]
except:
break


2.合唱团

题目描述:

有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?

输入描述:

每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

#coding=utf-8
n=input()
a=list(raw_input().split())
a=[int(i) for i in a]
ik,d=raw_input().split()
ik=int(ik)
d=int(d)
#动态规划,用两个数组,保留一正一负两个结果
mx=[[0 for i in range(n)] for i in range(ik)]
mn=[[0 for i in range(n)] for i in range(ik)]
m=0
for i in range(n):
mx[0][i]=mn[0][i]=a[i]
for k in range(1,ik):
for j in range(i-1,-1,-1):
if i-j>d:
break
mx[k][i]=max(mx[k][i],max(mx[k-1][j]*a[i],mn[k-1][j]*a[i]))
mn[k][i]=min(mn[k][i],min(mx[k-1][j]*a[i],mn[k-1][j]*a[i]))
m=max(m,mx[ik-1][i])
print m
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  动态规划 算法