运筹学中的经典动态规划
大家好,我是新来的小小编,小何同学。
//一个老年退休OI选手//
应我们大大老板的要求,小小编今天打算给大家带来一些在运筹学中比较基础的动态规划问题,也算在之前的小舟小编的基础上加一些东西吧!
小舟小编之前在公众号中已经简要地给大家介绍过了什么是动态规划问题,同时他也对三个比较基础的概念:阶段,状态,决策做了比较详细的解释,如果各位观众姥爷还没有看的话,可以去了解一下。
接下来就是我们大大老板给我的任务了,这也是几个比较典型的动态规划的例子,我们一起来看一下吧!
No.1
Investment Problem
这问题大概就是说我们有十个投资机会,有八个投资项目,我们可以在每个投资项目.上投资多个投资机会以获得相应的回报,那我们怎么才能获得最大的回报?
首先我们要知道动态规划的精髓:无后效性,就是前面做的决策并不会影响后面做的决策,也就是说我们前面的i个投资项目中花了j个投资的机会得出来的最大利益,无论我们在后面的项目中花费多少的投资机会,都不会引起前面的改变。那么很显然,我们可以推出第一-个的所有的投资方法,然后只要推出第二个,第三个,到最后一个,最后得出来解。
然后就是我们最为激动人心的coding time了!
上代码
using namespace std;
double value[9][5] = { {0,0,0,0,0},{0,4.1,5.8,6.5,6.8}, {0,1.8,3.0,3.9,4.5},{0,1.5,2.5,3.3,3.8},{0,2.2,3.8,4.8,5.5},{0,1.3,2.4,3.2,3.9},{0,4.2,5.9,6.6,6.8},{0,2.2,3.5,4.2,4.6},{0,1.0,1.7,2.3,2.8} };
double max(double x, double y)
{
if (x > y) return x;
return y;
}
int main()
{
double dp[9][11];//前i个投资项目用j个投资数的最大价值
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= 8; ++i)//项目
{
for (int j = 1; j <= 10; ++j)//到i时用了j个
{
for (int d = 0; d <= 4 && d <= j; ++d)//在i这里用了d个投资
{
for (int k = 0; k <= j - d; ++k)//前i-1个用了k个投资
{
dp[i][j] = max(dp[i][j], dp[i - 1][k] + value[i][d] + 0.5 * (j - k - d));
}
}
}
}
cout << dp[8][10];
return 0;
}
限于今天要讲的题目可能有点多,所以说没法给
大家很详细的讲解,下面的链 接中有我们大大老
板的PPT,里面很详细的介绍了怎么得出结果,怎
么分析,如果有不懂的地方,请移步下面链接。
然后课件里面有几道类似的题目,有兴趣的观众
姥爷们也可以去动动手。
No.2
Shortest Path Problem
在动态规划中,除了这种求最大值的算法之外,还有些是要求我们去求最小值的,这个就要求我们必须在开始处理数据之前必须把所有的储存空间先格式化一遍,不过这里的储存空间并不是意味着把它们全部格式化为零,而是格式化到无穷大,正因为我们要求的是最小值,所以说无穷大,相当于是不可能的。这跟我们求最大值时,把所有的初始化为零是一-个道理。
然后我们看一下例题。
这个题的意思就是说我们要从纽约去往洛杉矶,
中间可以途经很多其他的的地点,每两个节点之
间有一-个固定的路程长度,那么我们达到终点的
最短距离是多少呢?
我们先开始分析下,首先在我们制定数据的时候,我们把任意两个地,点之间的所有的距离都设为无穷大,这就表示这两个地点之间是不联通的,然后再通入我们输入的数据,把他们的距离再修改,如果他们的距离不是无穷大就说明他们之间是联通的。用我们定义的矩阵dp来记录两点之间的最短距离,下面的i和j所表示的是起点和终点,然后我们要做的就是不停的用k,也就是起点和终点之间可能经过的点来做松弛操作,把它们之间的最小距离,一步一步的刷新,最后得出子最优解。
但是请各位观众老爷要注意,这种算法的前面的三重循环必须有严格的顺序要求,因为如果我们一旦把循环的层次给改变,就可能导致当我们刷新-一个最优解的时候,他的子最优解并没有得出来,从而导致我们现在所求的最优解是错误的。所以请牢牢记住这一点。
下面就是我们的代码了,小何子,上代码 !
//Floyd算法,动态规划的思想,另外没有直接写入数据,因为实在是太多了。。。。。。//
include
using namespace std;
int dp[11][11];
int min(int x, int y)
{
if (x > y) return y;
return x;
}
int main()
{
int way_number;//路径的数量
cin >> way_number;
for (int i = 0; i <= 10; ++i) for (int j = 0; j <= 10; ++j) dp[i][j] = 0x3fffffff;//无穷大的话等于不通 for (int i = 1; i <= way_number; ++i) { int a, b, v; cin >> a >> b >> v;
dp[a][b] = v;
}
for (int i = 0; i <= 10; ++i)
dp[i][i] = 0;
for (int k = 1; k <= 10; ++k)
for (int i = 1; i <= 10; ++i)
for (int j = 1; j <= 10; ++j)
{
if (dp[i][j] < 0x3fffffff && dp[k][j] < 0x3fffffff)//防止爆int
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
}
cout << dp[1][10];
return 0;
这种求最小值的问题,本质上和那种求最大值的问题是相似的,但是我们要注意的是这种问题的初始化的方式是不同的,千万,千万,千万不能忘记了初始化。还有就是一定要把它的最初状态给修改下,-般就是在0的时候,还有的就是因为我们设置的是最大值,进行运算的时候,可能会爆掉储存的空间,所以说我们在进行预算的时候,最好加一个特判,否则也有可能会出现bug。
大大老板的课件里面还有几个跟这个比较相似的题目,各位看官老爷有兴趣的也可以去看一下。那么今天的全部内容到这里就结束了,小编也要期末复习了,可能也要隔段时间才给能大家送上新的推文吧。
另外,下面是小编代码和大大老板ppt链接:
https://pan.baidu.com/s/11FLHF0wF60Ud58dNUCw4g
提取码:yn5i
复制这段内容后打开百度网盘手机App,操作更方便哦