HUD 1257 最少拦截系统 / HDU 2084 数塔 (DP)

这个星期进入DP专题。

先是基本概念。。(摘抄课件,我自重= =)

动态规划(Dynamic Programming, DP)是解决某一类问题的一种方法,是分析问题的一种途径,而不是一种特殊算法(如线性规划是一种算法)。因此,在学习动态规划时,除了对基本概念和方法正确地理解外,应以丰富的想象力去建立模型,用创造性的技巧去求解。

动态规划三要素:阶段,状态,决策。

动态规划用于解决多阶段决策最优化问题,但是不是所有的最优化问题都可以用动态规划解答呢?

一般在题目中出现求最优解的问题就要考虑动态规划了,但是否可以用还要满足两个条件:

¢最优子结构(最优化原理)

当前状态是前面状态的完美总结

¢无后效性

什么是无后效性呢?

就是说在状态i求解时用到状态j而状态j就解有用到状态k…..状态N,而求状态N时有用到了状态i

解决动态规划问题的一般思路

(1)模型匹配法:

最先考虑的就是这个方法了。挖掘问题的本质,如果发现问题是自己熟悉的某个基本的模型,就直接套用,但要小心其中的一些小的变动,现在考题办都是基本模型的变形套用时要小心条件,三思而后行。这些基本模型在先面的分类中将一一介绍。

(2)三要素法

仔细分析问题尝试着确定动态规划的三要素,不同问题的却定方向不同:

先确定阶段的问题:数塔问题,和走路问题(详见解题报告)

先确定状态的问题:大多数都是先确定状态的。

先确定决策的问题:背包问题。(详见解题报告)

(3)寻找规律法

(4)边界条件法

找到问题的边界条件,然后考虑边界条件与它的领接状态之间的关系。这个方法也很起效。

(5)放宽约束和增加约束

这个思想是在陈启锋的论文里看到的,具体内容就是给问题增加一些条件或删除一些条件使问题变的清晰。

 

HDU两道典型例题。

先是简单的一维状态。比如HDU 1257,导弹拦截问题。

如果把题目换成“求最多能拦截的导弹数目”,那这道题就变得显而易见,求最长下降子序列类型DP。

很容易想到一个复杂度为n^2的方法,状态转移方程:

opt[i] = max{opt[j]+1, opt[i]} (0 <= j < i, aj >= ai)

很自然想到,既然求得了最长下降子序列,那是不是求出有几组这样的序列,输出组数就是答案了呢?

偏偏这样做是Wrong Answer。举出反例:

设导弹高度为:6 1 7 3 2

根据最初的方法求得,最长下降序列为:6 3 2/1/7,也即需要3套系统。但真正的答案是6 1/7 3 2,2套就够了!

换个思路。每个导弹都要拦截,每发射一发拦截炮弹,下一次发射高度不能超过上一发,也就是这套系统无法拦截比上一发炮弹高度更高的导弹。如此,考虑“最长上升子序列”。有点类似找父节点,最终答案就是最长上升子序列长度。(状态转移方程稍作改动即可)

完整代码

 1 /*
2 最长上升子序列(一维形式DP)
3 opt[i]=max(opt[j])+1, opt[i]) (0<=j<i 且num[j]<=num[i]) {最长非下降序列}
4 opt[i]=max(opt[j])+1, opt[i]) (0<=j<i 且num[j]>num[i]) {最长下降序列}
5 该算法复杂度为O(n^2)
6 */
7 #include <iostream>
8 using namespace std;
9
10 int num[30005], len[30005];
11
12 int main()
13 {
14 int m;
15 while(cin >> m) {
16 int ans = 0;
17 for(int i = 0; i < m; i++) {
18 cin >> num[i];
19 len[i] = 1;
20 }
21 for(int i = 0; i < m; i++) {
22 for(int j = 0; j < i; j++) {
23 if(num[i] > num[j]) {
24 len[i] = max(len[j] + 1, len[i]);
25 ans = max(ans, len[i]);
26 }
27 }
28 }
29 cout << ans << endl;
30 }
31 return 0;
32 }


然后是二维状态的。HDU 2084,数塔

这道题有个特点,从上到下分析,从下至上计算。不要被二维吓到,对于towel[i][j],我们用 i,j 表示当前数的坐标。

从下至上计算时,我们需要将当前数加上它的下一层左右两边的最大数,得状态转移方程:

towel[i][j] = max(towel[i + 1][j], towel[i + 1][j + 1]) + towel[i][j];

那么计算到最后,towel[0][0]就是答案。

(详细分析过程可以参考一下其他教程)

完整代码

 1 #include <iostream>
2 using namespace std;
3
4 int towel[100][100];
5
6 int main()
7 {
8 int t;
9 cin >> t;
10 while(t--) {
11 int n;
12 cin >> n;
13 for(int i = 0; i < n; i++)
14 for(int j = 0; j <= i; j++)
15 cin >> towel[i][j];
16 for(int i = n - 2; i >= 0; i--)
17 for(int j = 0; j <= i; j++)
18 towel[i][j] = max(towel[i + 1][j], towel[i + 1][j + 1]) + towel[i][j];
19 cout << towel[0][0] << endl;
20 }
21 return 0;
22 }

 

posted @ 2012-03-07 19:50  dgsrz  阅读(721)  评论(0编辑  收藏  举报