动态规划

原创 2016年05月30日 14:09:34

一 递推求解


1.题目1205:N阶楼梯上楼问题

时间限制:1 秒内存限制:128 兆特殊判题:否提交:4325解决:1742
题目描述:
N阶楼梯上楼问题:一次可以走两阶或一阶,问有多少种上楼方式。(要求采用非递归)
输入:
输入包括一个整数N,(1<=N<90)。
输出:
可能有多组测试数据,对于每组数据,
输出当楼梯阶数是N时的上楼方式个数。
样例输入:
4
样例输出:
5
来源:
2008年华中科技大学计算机保研机试真题

#include<stdio.h>

//保存登台阶的种类数
long long F[91]={0};

int main(){

    //台阶数
    int cnt=0;
    F[1]=1;
    F[2]=2;

    //1.预处理,求出登台阶种类数
    for(int i=3;i<=90;i++){
        F[i]=F[i-1]+F[i-2];
    }
    //2.输入台阶数
    while(scanf("%d",&cnt)!=EOF){
        //3.输出结果
        printf("%lld\n",F[cnt]);
    }

    return 0;
}

注意:
使用long long保存大数
输出long long用%lld

2.题目1451:不容易系列之一

时间限制:1 秒内存限制:128 兆特殊判题:否提交:1355解决:791
题目描述:
大家常常感慨,要做好一件事情真的不容易,确实,失败比成功容易多了!
做好“一件”事情尚且不易,若想永远成功而总从不失败,那更是难上加难了,就像花钱总是比挣钱容易的道理一样。
话虽这样说,我还是要告诉大家,要想失败到一定程度也是不容易的。比如,我高中的时候,就有一个神奇的女生,在英语考试的时候,竟然把40个单项选择题全部做错了!大家都学过概率论,应该知道出现这种情况的概率,所以至今我都觉得这是一件神奇的事情。如果套用一句经典的评语,我们可以这样总结:一个人做错一道选择题并不难,难的是全部做错,一个不对。
不幸的是,这种小概率事件又发生了,而且就在我们身边:
事情是这样的——HDU有个网名叫做8006的男性同学,结交网友无数,最近该同学玩起了浪漫,同时给n个网友每人写了一封信,这都没什么,要命的是,他竟然把所有的信都装错了信封!注意了,是全部装错哟!
现在的问题是:请大家帮可怜的8006同学计算一下,一共有多少种可能的错误方式呢?
输入:
输入数据包含多个多个测试实例,每个测试实例占用一行,每行包含一个正整数n(1《n<=20),n表示8006的网友的人数。
输出:
对于每行输入请输出可能的错误方式的数量,每个实例的输出占用一行。
样例输入:
2
3
样例输出:
1
2

#include<stdio.h>

//保存不同题目数的全错误数
long long num[21]={0};

int main(){
    //输入的题目数
    int cnt=0;
    num[1]=0;
    num[2]=1;

    //1.预处理求出前20错误数目
    for(int i=3;i<=20;i++){
        num[i]=(i-1)*num[i-1]+(i-1)*num[i-2];
    }
    //2.输入要计算的题目数
    while(scanf("%d",&cnt)!=EOF){
        //3.输出
        printf("%lld\n",num[cnt]);
    }

    return 0;
}

二 最长递增子序列( LIS)

1.题目1112:拦截导弹

时间限制:1 秒内存限制:32 兆特殊判题:否提交:3704解决:1818
题目描述:
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。
输入:
每组输入有两行,
第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),
第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。
输出:
每组输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。
样例输入:
8
300 207 155 300 299 170 158 65
样例输出:
6
来源:
2007年北京大学计算机研究生机试真题

#include<stdio.h>

//保存导弹高度
int dh[26]={0};
//保存以第i个导弹为结尾,能够制导的个数
int dcnt[26]={0};

//求最大值函数
int getMax(int a,int b){
    return a>b?a:b;
}

int main(){
    //导弹数目
    int cnt=0;
    //1.输入导弹数目
    while(scanf("%d",&cnt)!=EOF){
        //2.输入每个导弹的高度
        for(int i=0;i<cnt;i++){
            scanf("%d",dh+i);
        }
        //3.计算最大的拦截数
        for(int i=0;i<cnt;i++){
            //局部最大值
            int tmax=1;
            for(int j=0;j<i;j++){
                if(dh[j]>=dh[i]){
                    tmax=getMax(tmax,dcnt[j]+1);
                }
            }
            dcnt[i]=tmax;
        }
        //4.输出结果
        int ans=0;
        for(int i=0;i<cnt;i++){
            if(ans<dcnt[i]){
                ans=dcnt[i];
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

三 最长公共子序列( LCS)

1.题目1042:Coincidence

时间限制:1 秒内存限制:32 兆特殊判题:否提交:2796解决:1506
题目描述:
Find a longest common subsequence of two strings.
输入:
First and second line of each input case contain two strings of lowercase character a…z. There are no spaces before, inside or after the strings. Lengths of strings do not exceed 100.
输出:
For each case, output k – the length of a longest common subsequence in one line.
样例输入:
abcd
cxbydz
样例输出:
2
来源:
2008年上海交通大学计算机研究生机试真题

#include<stdio.h>
#include<string.h>
using namespace std;

//比较大小函数
int getMax(int a,int b){
    return a>b?a:b;
}

//保存l1的前i,l2的前j的最大字串数目
int dp[101][101]={0};

int main(){

    //保存输入字符串
    char l1[100];
    char l2[100];

    //1.输入两个字符串
    while(scanf("%s %s",l1,l2)!=EOF){
        //字符串的长度
        int l1_le=strlen(l1);
        int l2_le=strlen(l2);
        //清空
        for(int i=0;i<=l1_le;i++){
            dp[i][0]=0;
        }
        for(int j=0;j<=l2_le;j++){
            dp[0][j]=0;
        }

        //2.计算最大子字符串的长度
        for(int i=1;i<=l1_le;i++){
            for(int j=1;j<=l2_le;j++){
                //如果第i 第j个字符相同
                if(l1[i-1]==l2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                //如果第i 第j个字符不同
                else{
                    dp[i][j]=getMax(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        //3.输出结果
        printf("%d\n",dp[l1_le][l2_le]);

    }
    return 0;
}

注意:
数组下标从1开始,要谨慎使用。但有时使用却很方便。


五 动态规划问题分析举例


1.题目1452:搬寝室

时间限制:1 秒内存限制:128 兆特殊判题:否提交:1614解决:656
题目描述:
搬寝室是很累的,xhd深有体会.时间追述2006年7月9号,那天xhd迫于无奈要从27号楼搬到3号楼,因为10号要封楼了.看着寝室里的n件物品,xhd开始发呆,因为n是一个小于2000的整数,实在是太多了,于是xhd决定随便搬2*k件过去就行了.但还是会很累,因为2*k也不小是一个不大于n的整数.幸运的是xhd根据多年的搬东西的经验发现每搬一次的疲劳度是和左右手的物品的重量差的平方成正比(这里补充一句,xhd每次搬两件东西,左手一件右手一件).例如xhd左手拿重量为3的物品,右手拿重量为6的物品,则他搬完这次的疲劳度为(6-3)^2 = 9.现在可怜的xhd希望知道搬完这2*k件物品后的最佳状态是怎样的(也就是最低的疲劳度),请告诉他吧。
输入:
每组输入数据有两行,第一行有两个数n,k(2<=2*k<=n<2000).第二行有n个整数分别表示n件物品的重量(重量是一个小于2^15的正整数).
输出:
对应每组输入数据,输出数据只有一个表示他的最少的疲劳度,每个一行.
样例输入:
2 1
1 3
样例输出:
4

#include<stdio.h>
#include<algorithm>
using namespace std;

//求最小值函数
int getMin(int a,int b){
    return a>b?b:a;
}
//保存n个物品
int siz[2000]={0};
//爆粗疲劳度
int pl[1001][2001]={0};


int main(){
    //一共的物品数目n,希望搬k对
    int n=0;
    int k=0;

    //1.输入n,k
    while(scanf("%d %d",&n,&k)!=EOF){
        //2.输入n个物品
        for(int i=0;i<n;i++){
            scanf("%d",siz+i);
        }
        //3.进行排序(升序)
        sort(siz,siz+n);
        //4.求出每个状态的疲劳度
        for(int i=1;i<=k;i++){
            for(int j=2*i;j<=n;j++){
                if(j==2*i){
                    pl[i][j]=pl[i-1][j-2]+(siz[j-1]-siz[j-2])* (siz[j-1]-siz[j-2]);
                }else{
                    pl[i][j]=getMin(pl[i][j-1],pl[i-1][j-2]+(siz[j-1]-siz[j-2])* (siz[j-1]-siz[j-2]));
                }
            }
        }
        //5.输出结果
        printf("%d\n",pl[k][n]);
    }
    return 0;
}

2.题目1453:Greedy Tino(未AC)

时间限制:1 秒内存限制:128 兆特殊判题:否提交:969解决:312
题目描述:
Tino wrote a long long story. BUT! in Chinese…
So I have to tell you the problem directly and discard his long long story. That is tino want to carry some oranges with “Carrying pole”, and he must make two side of the Carrying pole are the same weight. Each orange have its’ weight. So greedy tino want to know the maximum weight he can carry.
输入:
The first line of input contains a number t, which means there are t cases of the test data.
for each test case, the first line contain a number n, indicate the number of oranges.
the second line contains n numbers, Wi, indicate the weight of each orange
n is between 1 and 100, inclusive. Wi is between 0 and 2000, inclusive. the sum of Wi is equal or less than 2000.
输出:
For each test case, output the maximum weight in one side of Carrying pole. If you can’t carry any orange, output -1. Output format is shown in Sample Output.
样例输入:
1
5
1 2 3 4 5
样例输出:
Case 1: 7

#include<stdio.h>
#define OFFSET 2000
#define INF 0x7fffffff

//得到最大值
int getMax(int a,int b,int c){
    if(a>b)
        return a>c?a:c;
    else
        return b>c?b:c;
}

int main(){
    //保存testcase的个数
    int t=0;
    //保存橘子的个数
    int s_cnt=0;
    //保存平衡橘子的数组
    int dp[101][4001]={0};
    //保存每个句子的数量
    int wei[100];
    //保存是否有重量为0的橘子
    bool haveZero=false;

    //1.输入测试用例的个数
    scanf("%d",&t);
    for(int k=0;k<t;k++){
        haveZero=false;
        //2.输入当前测试用例的橘子数目
        scanf("%d",&s_cnt);

        int cnt=0;
        //3.输入每个橘子的重量
        for(int i=0;i<s_cnt;i++){
            scanf("%d",wei+cnt);
            if(wei[cnt]==0){
                cnt--;
                haveZero=true;
            }
            cnt++;
        }
        s_cnt=cnt;

        //4.初始化平衡橘子的数组为负无穷,不能初始化为0,同时设置dp[0][0]为0
        for(int i=-2000;i<=2000;i++)
            dp[0][i+OFFSET]=-INF;
        dp[0][0+OFFSET]=0;
        //5.计算平衡橘子数组
        for(int i=1;i<=s_cnt;i++){
            //用来循环重量-2000--2000
            for(int j=-2000;j<=2000;j++){
                //保证j+OFFSET-wei[i]与j+OFFSET+wei[i]在-2000--2000范围内
                int tem1=-INF;
                int tem2=-INF;
                if(j-wei[i-1]>=-2000&&dp[i-1][j+OFFSET-wei[i-1]]!=-INF){
                    tem1=dp[i-1][j+OFFSET-wei[i-1]]+wei[i-1];
                }
                if(j+wei[i-1]<=2000&&dp[i-1][j+OFFSET+wei[i-1]]!=-INF){
                    tem2=dp[i-1][j+OFFSET+wei[i-1]]+wei[i-1];
                }
                dp[i][j+OFFSET]=getMax(tem1,tem2,dp[i-1][j+OFFSET]);//加在第一框,加在第二框,两边都不加
            }
        }
        //6.输出结果
        printf("Case %d:",k+1);
        if(dp[s_cnt][0+OFFSET]==0){
            if(haveZero==true){
                printf("0\n");
            }else{
                printf("-1\n");
            }
        }else{
            printf("%d\n",dp[s_cnt][0+OFFSET]/2);
        }
    }
    return 0;
}

注意事项:
初始化橘子差不能初始化为0,因为需要考虑橘子质量为0的情况。
因为递归需要有i-1的情况,所以为了简单,循环变量需要从1开始。

版权声明:本文为博主原创文章,未经博主允许不得转载。

如何实现动态规划?——TWO

忙碌了一天,是时候继续动态规划的问题了,昨天写了一些对动态规划的入门理解,尽管我文笔略差,但是自我感觉打得比方还是比较形象的;同时也转载了一个讲述动态规划的博文,但是在今天通读后,感觉这篇文章优点和缺...
  • f_zyj
  • f_zyj
  • 2016年03月01日 22:11
  • 875

教你彻底学会动态规划——入门篇

动态规划相信大家都知道,动态规划算法也是新手在刚接触算法设计时很苦恼的问题,有时候觉得难以理解,但是真正理解之后,就会觉得动态规划其实并没有想象中那么难。网上也有很多关于讲解动态规划的文章,大多都是叙...
  • baidu_28312631
  • baidu_28312631
  • 2015年08月11日 13:26
  • 100834

动态规划总结【模板】

最长递增子序列 最大连续子序列和 最大连续子矩阵和 最大M个连续子段的和 最大不连续子序列和 最长公共子序列 最长回文子序列 最长回文子串 最小编辑距离 01背包 完全背包 多重背包 二维费用背包 切...
  • u011676797
  • u011676797
  • 2015年05月01日 23:49
  • 1931

动态规划之矩阵路径问题

动态规划 最小路径和问题
  • yu280265067
  • yu280265067
  • 2016年03月11日 11:59
  • 1474

这个动态规划分析的很详细(转载)

动态规划 动态规划 转自:http://www.cnblogs.com/raichen/p/5772056.html 通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常...
  • yuxin6866
  • yuxin6866
  • 2016年09月11日 23:48
  • 762

动态规划总结——经典问题总结

动态规划总结——经典问题总结 本文着重讨论状态是如何表示,以及方程是怎样表示的。当然,还附上关键的,有可能作为模板的代码段。但有的代码的实现是优化版的。 经典问题总结 最长上升子序列(LIS) 问题描...
  • qfikh
  • qfikh
  • 2016年07月19日 13:08
  • 2753

杭电动态规划题

Monkey and Banana Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other...
  • king_cannon_fodder
  • king_cannon_fodder
  • 2017年06月05日 21:26
  • 174

hdu 动态规划46题

1.Robberies  连接 :http://acm.hdu.edu.cn/showproblem.php?pid=2955      背包;第一次做的时候把概率当做背包(放大100000倍...
  • youngyangyang04
  • youngyangyang04
  • 2014年08月18日 00:01
  • 992

非常好的动态规划总结,DP总结

总结的非常好,谢谢作者。 http://cppblog.com/menjitianya/archive/2015/10/23/212084.html 目录   一、动态规...
  • mmc2015
  • mmc2015
  • 2017年06月22日 09:31
  • 3419

教你彻底学会动态规划——进阶篇

在我的上一篇文章中已经详细讲解了动态规划的原理和如何使用动态规划解题。本篇文章,我将继续通过例子来让大家更加熟练地使用动态规划算法。     话不多说,来看如下例题,也是在动态规划里面遇到过的最频繁的...
  • baidu_28312631
  • baidu_28312631
  • 2015年08月11日 20:29
  • 8691
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:动态规划
举报原因:
原因补充:

(最多只允许输入30个字)