目录
知识讲解
动态规划(Dynamic programming,简称DP)是运筹学的一个分支,是求解“决策过程最优值”问题的一种重要算法。
动态规划的基本思想
动态规划通常应用于最优化问题。
和分治法一样,动态规划也是把一个复杂问题分解成相对简单的“子问题”的方式求解,再组合出答案的方法。不过,分治法是将问题划分成一些“独立”的子问题,递归地求解各子问题,然后再合并子问题的解而得到原问题的解,如二分查找、快速排序等问题。与此不同,动态规划适用于子问题不是独立的情况,也就是子问题包含公共的“子子问题”。动态规划算法对每个子子问题只求解一次,并且立刻将其结果保存到一张“表”中,从而避免后面每次遇到子子问题时重复去计算。
随堂练习
第一题 城市交通
【问题描述】
有 n 个城市,编号 1~n,有些城市之间有路相连,有些则没有,有路则会有一个距离。图 9.12-1 所示为一个含有 11 个城市的交通图,连线上的数(权)表示距离。现在规定只能从编号小的城市到编号大的城市。问:从编号为 1 的城市到编号为n 的城市之间的最短距离是多少?
【输入格式】
第 1 行为 n,表示城市数,n≤100。
下面的 n 行是一个 n×n 的邻接矩阵map[i,j],其中 map[i,j]=0 表示城市 i和城市 j 之间没有路相连,否则为两者之间的距离。
【输出格式】
一行一个数,表示最短距离。数据保证一定可以从城市 1 到城市 n。
【输入样例】
11
0 5 3 0 0 0 0 0 0 0 0
5 0 0 1 6 3 0 0 0 0 0
3 0 0 0 8 0 4 0 0 0 0
0 1 0 0 0 0 0 5 6 0 0
0 6 8 0 0 0 0 5 0 0 0
0 3 0 0 0 0 0 0 0 8 0
0 0 4 0 0 0 0 0 0 3 0
0 0 0 5 5 0 0 0 0 0 3
0 0 0 6 0 0 0 0 0 0 4
0 0 0 0 0 8 3 0 0 0 3
0 0 0 0 0 0 0 3 4 3 0
【输出样例】
13
【问题分析】
逆向思考,现在想要从城市1到达城市11,则只能从城市8、9或10中转过去,如果知道了从城市1到城市8、9和10的最短距离,那么只要把这3个最短距离分别加上这3个城市与城市11之间的距离,再从中取一个最小值即可。这样一来,问题就变成了求城市1到城市8、9、10的最短距离,而这3个子问题与原问题是完全一致的,只是问题的规模缩小了一点。如何求城市1到城市8的最短距离呢?想法与刚才一样,如果知道了城市1到城市4和5的最短距离,那么到城市8的最短距离就是到城市4的最短距离加上5以及到城市5的最短距离加上5当中较小的那个值。而如何求城市4和5的最短距离呢?…如此下去,直到求城市1到城市2和3的最短距离,而这个值是已知的(因为城市1和2、3之间有直接的边相连)。这种解题思想就是“动态规划”。
#include<bits/stdc++.h>
using namespace std;
const int N = 110, INF = 1 << 30;//INF表示2^30
int a[N][N], dp[N], n;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j) scanf("%d", &a[i][j]);
for(int i = 2; i <= n; ++i) dp[i] = INF;
for(int i = 2; i <= n; ++i)
for(int j = 1; j < i; ++j)
if(a[i][j]) dp[i] = min(dp[i], dp[j] + a[i][j]);
printf("%d\n", dp[n]);
return 0;
}
第二题 拦截导弹
【问题描述】
某国为了防御敌国的导弹袭击,研究出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意高度,但是以后每一发炮弹都不能高于前一发的高度。
某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹的枚数和导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,以及如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
【输入格式】
第 1 行为正整数 n,表示导弹的枚数。
第 2 行为 n 个正整数,表示依次飞来的导弹高度(不大于 30000,单位:米),每两个数据之间有一个空格。
【输出格式】
第 1 行 1 个正整数,表示一套系统最多能拦截多少导弹。
第 2 行 1 个正整数,表示要拦截所有导弹最少要配备多少套拦截系统。
【输入样例】
8
389 207 155 300 299 170 158 65
【输出样例】
6
2
第三题 数字三角形
【问题描述】
如下所示为一个数字三角形:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
请编程计算从顶至底部某处的一条路径,使该路径所经过的数字的总和最大。约定:
(1) 每一步可沿直线向下或右斜线向下走;
(2) 1≤三角形行数≤100;
(3) 三角形中的数字为整数 0,1,…,99。
【输入格式】
第一行为一个自然数,表示数字三角形的行数 n;
接下来的 n 行,表示一个数字三角形。同一行的两数之间都有一个空格。
【输出格式】
输出一行一个整数,表示要求的最大总和。
【输入样例】
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
【输出样例】
30
第四题 摆花
【问题描述】
小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 n 种花,从 1 到 n 标号。为了在门口展示出更多种花,规定第i种花不能超过a i 盆,同一种花摆放在一起,且不同种类的花需按标号从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。
【输入格式】
第 1 行包含 2 个正整数 n 和 m,中间用一个空格隔开。
第 2 行有 n 个整数依次表示 a i ,每两个整数之间用一个空格隔开。
【输出格式】
输出一行一个整数,表示有多少种摆放方案。
注意:因为方案数可能很多,请输出方案数对 1000007 取模的结果。
【输入样例】
2 4
3 2
【输出样例】
2
【样例说明】
有两种摆花的方案,分别是(1,1,1,2)和(1,1,2,2)。括号里的 1 和 2 表示两种花,如第一个方案是前三个位置摆第一种花,第四个位置摆第二种花。
结语
DP吧,不难,希望大家能把这篇文章看明白,早日成为C++大神,拜拜┏(^0^)┛