动态规划总结
通过做题学习动态规划,动态规划是根据子问题来找到最优解,每一个子问题都是一个最优解,根据当前状态和子问题来选择最优解。
一.使用二维数组解决
Problem17:数字三角形
TimeLimit:1Ms| MemoryLimit:128MB
Difficulty:1
Description
示出了一个数字三角形。请编一个程序计算从顶至底的某处的一条路
径,使该路径所经过的数字的总和最大。
每一步可沿左斜线向下或右斜线向下走;
1<三角形行数<25;
三角形中的数字为整数<1000;
Input
第一行为N,表示有N行
后面N行表示三角形每条路的路径权
Output
路径所经过的数字的总和最大的答案
SampleInput
5
7
38
8 1 0
2 7 4 4
4 5 2 6 5
SampleOutput
30
解题思路:从上到下与从下到上解决是一样的,
(从上到下):如果要找出一条权值最大的路径,那么就要保证当前的与以前走的之和最大,
感觉有点像贪心,但是贪心只是寻找下一步最大的,所以贪心思想是错误的,
做动规就要找到状态转移方程:以二维数组表示状态,
从当前点到下一行可走两种:向下和向右下。那么当前的选择一定是上一层向上和向左上,
其中最大的那个。
则状态转移方程:dp[i][j]= max(dp[i-1][j], dp[i-1][j-1]) + cur[i][j].
最终求得的最大路径为max(dp[n][i](1<=i<=n))
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=171
聪明的kk
时间限制:1000 ms | 内存限制:65535 KB
难度:3
描述
聪明的“KK”
非洲某国展馆的设计灵感源于富有传奇色彩的沙漠中陡然起伏的沙丘,体现出本国不断变换和绚丽多彩的自然风光与城市风貌。展馆由五部分组成,馆内影院播放名为《一眨眼的瞬间》的宽银幕短片,反映了建国以来人民生活水平和城市居住环境的惊人巨变。
可移动“沙丘”变戏法的灵感源于其独特而雄伟的自然景观——富于传奇色彩的险峻沙丘。宏伟的结构、可循环的建材,与大自然相得益彰。环绕一周,发现它正是从沙丘那不断变换的形态中汲取灵感的。外形逼真到无论从哪个角度去观察,都能清楚地辨识出沙丘的特征。
它“坡面”高达20米,微风吹来,你是否感觉到沙的流动?用手去触碰,却发现原来是“魔术戏法”。它表面的不锈钢面板呈现出一种富于变幻的色彩,从不同角度观察,呈现不同色泽,由此来模仿流动沙丘的光感。
走进第三展厅有一个超大的屏幕,通过奇妙的特效,让观众犹如亲身来到浩瀚的沙漠。更为奇妙的是,只见一个小动物“KK”正从沙漠区域(矩形)的左上角沿着向右或向下的方向往右下角跑去。KK太聪明了,它居然能在跑的过程中会选择吃掉尽可能多的虫子线路。
你知道它吃掉多少虫子吗?
输入
第一行:NM (1≤N M≤20 0≤Xij≤500(i=1,2„.N,j=1,2„,M)
)表示沙漠是一个N*M的矩形区域
接下来有N行:每行有M个正整数,Xi1Xi2 ……Xim 表示各位置中的虫子数(单个空格隔开)
假设“KK”只能向右走或向下走。
输出
输出有一个整数,表示“KK”吃掉最多的虫子数。
样例输入
34
31 2 8
53 4 6
10 2 3
样例输出
24
仔细阅读题目发现:kk每次可以向下和向右移动,与数字三角形类似。
所以思路是一样的,依然采用自上向下搜索,
状态转移方程:dp[i][j]= max(dp[i-1][j], dp[i][j-1])+num[i][j],注意与数字三角的方程不一样
#include <stdio.h>
#include <memory.h>
int num[21][21];
int dp[21][21];
int max(int a, int b)
{
return a>b?a:b;
}
int main()
{
int n, m, i, j;
while (~scanf("%d %d", &n, &m))
{
memset(dp, 0, sizeof(dp));
for (i=1; i<=n; ++i)
for (j=1; j<=m; ++j)
scanf("%d", &num[i][j]);
for (i=1; i<=n; ++i)
{
for (j=1; j<=m; ++j)
{
dp[i][j] = max(dp[i][j-1], dp[i-1][j])+num[i][j];
}
}
int res = -1;
for (int i=1; i<=m; ++i)
{
if (res < dp[n][i])
res = dp[n][i];
}
printf("%d\n", res);
}
return 0;
}
二.使用一维数组解决
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=79
拦截导弹
时间限制:3000 ms | 内存限制:65535 KB
难度:3
描述
某国为了防御敌国的导弹袭击,发展中一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于等于前一发的高度。某天,雷达捕捉到敌国导弹来袭。由于该系统还在试用阶段,所以只用一套系统,因此有可能不能拦截所有的导弹。
输入
第一行输入测试数据组数N(1<=N<=10)
接下来一行输入这组测试数据共有多少个导弹m(1<=m<=20)
接下来行输入导弹依次飞来的高度,所有高度值均是大于0的正整数。
输出
输出最多能拦截的导弹数目
样例输入
2
8
389207 155 300 299 170 158 65
3
8834 65
样例输出
6
2
首先要理解题意:只要拦截当前导弹,那么以后只能拦截比当前导弹高度低的导弹,
要想拦截的导弹最多,就要尝试拦截所有导弹,并找到最大拦截数。
由于每次都是动态的决定拦截数,所以选择动规。
初始:假设拦截dao[i],则dp[i]= 1
状态转移方程:dp[i]= max(dp[j]+1), (dao[i] < dao[j]) (j < i)
说明一下,当前拦截导弹为dao[i],那么如果dao[j]> dao[i],那么当前拦截数一定为,
当前拦截的与第j次拦截数+1中的最大值,为什么要加一,因为当前拦截到一个导弹,就应多加一个导弹。
#include <stdio.h>
#include <string.h>
#include <memory.h>
#define MAX 22
int m[MAX];
int d[MAX], sum;
inline int mymax(int n1, int n2)
{
return n1>n2?n1:n2;
}
int DP()
{
int i, j;
memset(m, 0, sizeof(m));
for (i=0; i<sum; ++i)
{
m[i] = 1; // 假设拦截当前导弹
for (j=0; j<i; ++j)
if (d[i] < d[j]) // d[i]<d[j] 那么比较拦截数肯定为j次拦截的+1
m[i] = mymax(m[j]+1, m[i]); // m[i]保存拦截的最大数(j以前)
}
j = m[0];
for (i=1; i<sum; ++i)
j = mymax(j, m[i]);
return j;
}
int main()
{
int n, i;
scanf("%d", &n);
while (n-- > 0)
{
scanf("%d", &sum);
for (i=0; i<sum; ++i)
scanf("%d", &d[i]);
printf("%d\n", DP());
}
return 0;
}
矩形嵌套
难度:4
-
描述
- 有n个矩形,每个矩形可以用a,b来描述,表示长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d或者b<c,a<d(相当于旋转X90度)。例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)中。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。 输入
-
第一行是一个正正数N(0<N<10),表示测试数据组数,
每组测试数据的第一行是一个正正数n,表示该组测试数据中含有矩形的个数(n<=1000)
随后的n行,每行有两个数a,b(0<a,b<100),表示矩形的长和宽
输出
- 每组测试数据都输出一个数,表示最多符合条件的矩形数目,每组输出占一行 样例输入
1 10 1 2 2 4 5 8 6 10 7 9 3 1 5 8 12 10 9 7 2 2
-
样例输出
5
乍一看确实想不出如何写出它的状态转移方程,如果不对矩形进行处理的话很难想到,
大致题意:给出很多矩形,让你选择,保证当前矩形能够嵌套前一个矩形,或者当前矩形长大于前一个宽,或者当前宽大于长,如果对每次输入都处理成长大于宽,那么以后比较就少比较一次,然后再去考虑方程。
解题步骤:
1.对输入的矩形进行排序。
2.状态转移方程:dp[i]= max(dp[j]+1) (i > j, RCi嵌套RCj)
假设当前选择RCi,初始:dp[i]=1,那么对于以前的如果当前矩形可以嵌套RCj那么当前嵌套数,必为:dp[i]与dp[j]+1最大的一个。
代码:
#include <stdio.h>
#include <algorithm>
#include <memory.h>
using namespace std;
int dp[1002];
struct Rect
{
int x, y;
};
bool cmp(Rect rc1, Rect rc2)
{
if (rc1.x < rc2.x)
return true;
else if (rc1.x == rc2.x)
return rc1.y < rc2.y;
return false;
}
Rect rc[1002];
int main()
{
int i, j, m, n, w, h;
scanf("%d", &n);
while (n--)
{
scanf("%d", &m);
memset(dp, 0, sizeof(dp));
for (i=0; i<m; ++i)
{
scanf("%d %d", &w, &h);
if (w > h)
{
j = w;
w = h;
h = j;
}
rc[i].x = w;
rc[i].y = h;
}
sort(rc, rc+m, cmp);
for (i=0; i<m; ++i)
{
dp[i] = 1;
for (j=0; j<i; ++j)
{
if (rc[i].x > rc[j].x && rc[i].y > rc[j].y)
{
if (dp[i] < dp[j]+1)
dp[i] = dp[j]+1;
}
}
}
int res = -1;
for (i=0; i<m; ++i)
{
if (res < dp[i])
res = dp[i];
}
printf("%d\n", res);
}
return 0;
}
总结:
动态规划现在只学了一点皮毛,碰到动规的题主要是找出状态转移方程,还有如何对数据进行处理,怎样保存结果。
最主要的是:怎样辨别此题是个动态规划的题。