zoukankan      html  css  js  c++  java
  • DP

    dp要素:

          初始状态+目标状态+状态转移方程

     dp分类:

        线性dp:

           根据左右上下的状态进行转移

           有时候用二维。仔细观察题目的联系,看怎么转移。

           题目:

    问题 A: 【动态规划】数字三角形
    时间限制: 1 Sec  内存限制: 128 MB
    提交: 120  解决: 65
    [提交] [状态] [讨论版] [命题人:yuanmeng]
    题目描述
            7
          3   8
        8   1   0
      2   7   4   4
    4   5   2   6   5
          (图1)
    图1给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
    注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的那个数或者右边的那个数。输入输入的是一行是一个整数N (1 < N <= 1000),给出三角形的行数。下面的N行给出数字三角形。数字三角形上的数的范围都在0和100之间。
    输入
    输入的是一行是一个整数N (1 < N <= 1000),给出三角形的行数。下面的N行给出数字三角形。数字三角形上的数的范围都在0和100之间。
    输出
    输出最大的和。
    样例输入 Copy
    5
    7
    3 8
    8 1 0 
    2 7 4 4
    4 5 2 6 5
    样例输出    Copy
    30
    View Code

           代码:

    #include <bits/stdc++.h>
    #define MAXN 1005
    using namespace std;
    int f[MAXN][MAXN];
    int i,n,m,j,a;
    int main(){
        scanf("%d",&n);
        for(j=1;j<=n;j++)
        {
            for(i=1;i<=j;i++)
            {
                scanf("%d",&a);
                if(f[j-1][i]+a>=f[j-1][i-1]+a)
                f[j][i]=f[j-1][i]+a;
                else
                f[j][i]=f[j-1][i-1]+a;
            }
        }
        for(i=2;i<=n;i++)
         {
            if(f[n][i]<f[n][i-1])
              f[n][i]=f[n][i-1];
         }
         printf("%d",f[n][n]);
    }
    View Code

                题目:

    问题 B: 【动态规划】最长上升子序列
    时间限制: 1 Sec  内存限制: 64 MB
    提交: 76  解决: 34
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    给定一个整数序列A1A2A3….An。求它的一个递增子序列,使子序列的元素个数尽量多,元素不一定要求连续。 
    输入
    第1行:1个整数n(1<=n<=5000),表示序列中元素的个数. 
    第2行-n+1行:每行1个整数x(-1000<=x<=1000),第i+1行表示序列中的第i个元素。 
    输出
    第1行:1个整数k,表示最长上升子序列的长度。 
    第2行:k个用单个空格分开的整数,表示找到了最长上升子序列。如果有多个长度等于k的子序列,则输出最靠前的1个。 
    样例输入 Copy
    8
    1
    3
    2
    4
    3
    5
    4
    6
    样例输出    Copy
    5
    1 3 4 5 6
    View Code

             代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M =5010;
    #define ri register int 
    int val[M],f[M],c[M],p,n,ans;
    void out(int a)
    {
        if(c[a])
        {
            out(c[a]);
            printf(" ");
        }
        printf("%d",val[a]);
    }
    int main(){
        scanf("%d",&n);
        for(ri i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
        }
        for(ri i=1;i<=n;i++)
        {
             int trmp=0;
            for(ri j=1;j<i;j++)
            {
                if(val[i]>val[j]&&trmp<f[j])
                 trmp=f[j],c[i]=j;
            }
            f[i]=trmp+1;
            if(ans<f[i]) ans=f[i],p=i;
        }
        printf("%d
    ",ans);
        out(p);
    }
    View Code

            题目:

    问题 D: 【动态规划】渡轮问题
    时间限制: 1 Sec  内存限制: 64 MB
    提交: 53  解决: 25
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    Palmia 河在某国从东向西流过,并把该国分为南北两个部分。河的两岸各有 N 座城市,且北岸的每一个城市都与南岸的某个城市是友好城市,而且友好关系是一一对应的。现在要求在两个友好城市之间建立一条航线,但由于天气的原因,所有航线都不能相交,因此,就不可能给所有的友好城市建立航线。 问题:当给出城市个数和友好关系的信息之后,选择一种修建航线的方案,能建最多的航线而不相交。 
     
    输入
    第1行:2个空格分开的整数X, Y,X (10≤X≤6000)表示Palmia河岸的长度,Y(10≤Y≤ 100)表示河的宽度 第2行:1个整数N (1≤N≤5000),表示在河两岸分别有多少座城市 接下来N行:每行2个空格分开的非负整数C、D(C,D ≤X),表示河两岸的一对友好城市从河最西端算起的坐标(C表示北岸的城市,D表示南岸的城市),在同一岸上没有两座城市的坐标相同。
    输出
    第1行: 最多能建立航线的数量K 接下来K行:每行2个用1个空格分开的整数,按北岸城市的坐标从小到大的顺序输出建立航线的K对友好城市的坐标。若有多种方案,选择北岸城市(从后往前数起)编号大的那种方案
    样例输入 Copy
    30 4
    7
    22 4
    2 6
    10 3
    15 12
    9 8
    17 17
    4 2
    样例输出    Copy
    4
    4 2
    10 3
    15 12
    17 17
    View Code

             代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M =5010;
    #define ri register int 
    int n,m,t,g,f[M],c[M];
    struct dian{
        int x,y;
    }p[M];
    int ans;
    void out(int a)
    {
        if(c[a])
        {
            out(c[a]);
            printf("
    ");
        }
        printf("%d %d",p[a].x,p[a].y);
    }
    int cmp1(const dian &a,const dian &b)
    {
        return a.y<b.y;
    }
    int main(){
        scanf("%d%d%d",&n,&m,&t);
        for(ri i=1;i<=t;i++)
        {
            scanf("%d%d",&p[i].x,&p[i].y);
        }
        sort(p+1,p+1+t,cmp1);
        for(ri i=1;i<=t;i++)
        {
            int trmp=0;
            for(ri j=1;j<i;j++)
            {
                if(p[i].x>p[j].x&&f[j]>trmp)
                trmp=f[j],c[i]=j;
            }
            f[i]=trmp+1;
            if(f[i]>ans) ans=f[i],g=i;
        }
        printf("%d
    ",ans);
        out(g);
    }
    View Code

            题目:

    问题 E: 【动态规划】合唱队形
    时间限制: 1 Sec  内存限制: 64 MB
    提交: 28  解决: 18
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1≤ i ≤K)使得T1<T2< ......< Ti-1 < Ti >Ti+1 >......>TK。 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。 
    输入
    输入的第一行是一个整数N(2<=N<=100),表示同学的总数。 第二行有n个整数,用空格分隔,第i个整数Ti(130≤Ti≤230)是第i位同学的身高(厘米)。 
    输出
    输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。 
    样例输入 Copy
    8
    186 186 150 200 160 130 197 220
    
    样例输出    Copy
    4
    提示
    数据规模 对于50%的数据,保证有n≤20; 对于全部的数据,保证有n≤100
    View Code

           代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M =10005;
    #define ri register int
    int f[M],p[M],n,val[M],ans=INT_MAX;
    int main(){
        scanf("%d",&n);
        for(ri i=1;i<=n;i++)
        {
            scanf("%d",&val[i]);
        }
        for(ri i=1;i<=n;i++)
        {
             int trmp=0;
            for(ri j=1;j<i;j++)
            {
                if(f[j]>trmp&&val[i]>val[j])
                 trmp=f[j];
            }
            f[i]=trmp+1;
        }
        for(ri i=1;i<=n;i++) f[i]=i-f[i];
        for(ri i=n;i>=1;i--)
        {
            int trmp=0;
            for(ri j=n;j>i;j--)
            if(p[j]>trmp&&val[j]<val[i])
              trmp=p[j];
             p[i]=trmp+1; 
        }
        for(ri i=n;i>=1;i--) p[i]=(n-i+1)-p[i];
        for(ri i=1;i<=n;i++)
          if(ans>p[i]+f[i]) ans=p[i]+f[i];
          printf("%d
    ",ans);
          return 0;
    }
    View Code

    题目:

    问题 F: 【动态规划】拦截导弹
    时间限制: 1 Sec  内存限制: 64 MB
    提交: 42  解决: 17
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。 输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
    输入
    第1行:依次输入若干个导弹的高度H(1≤H≤30000),导弹的个数N≤5000
    输出
    第1行:一个整数,表示单枚炮弹能拦截多少导弹 第2行:一个整数,表示拦截所有导弹最少要配备多少套这种导弹拦截系统
    样例输入 Copy
    389 207 155 300 299 170 158 65
    样例输出    Copy
    6
    2
    提示
    第2问最直观的算法是贪心,但是反例也容易找到,如: 6 5 1 7 3 2 如果第一次打6 5 3 2,显然还要打两次,而最好的方案是6 5 1/7 3 2
    View Code

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M =5010;
    #define ri register int
    int n,m,val[M],f[M],p[M],ans,ans2;
    int main(){
        n=1;
         while(scanf("%d",&val[n])!=-1) n++;
        for(ri i=1;i<=n-1;i++)
        {
            int trmp=0,trm=0;
            for(ri j=1;j<=i;j++)
            {
                if(trmp<f[j]&&val[j]>val[i])
                  trmp=f[j];
                  if(trm<p[j]&&val[j]<val[i])
                  trm=p[j];
            }
            f[i]=trmp+1; p[i]=trm+1;
            if(ans<f[i]) ans=f[i];
            if(ans2<p[i])ans2=p[i];
        }
        printf("%d
    %d",ans,ans2);
    }
    View Code

    题目:

    问题 H: 【动态规划】求最长公共子序列
    时间限制: 1 Sec  内存限制: 64 MB
    提交: 20  解决: 13
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    一个给定序列的子序列是在该序列中删去若干元素(也可以不删去)后得到的序列。例如Z="BCDB" 就是X="ABCBDAB"的一个子序列,而Z="CBBD"则不是X的子序列。 给定三个序列 X,Y和Z,如果Z既是X的一个子序列又是Y的一个子串,则称Z是X和Y的公共子序列。 
    例如X="ABCBDAB",Y=BDCABA",则序列"BCA"即为X和Y的一个公共子序列,但不是X和Y的最长公共子序列(LCS),因为还有比它更长的公共子序列"BCBA"。事实上"BCBA"是X和Y的一个LCS ,"BDAB"也是一个LCS。 
    现输入两个序列X和Y,要求出X和Y的最长公共子序列。 
    输入
    第1行:两个用字符串表示的序列X和Y。1<=X.size(),Y.size()<=1000,两个序列之间用1个空格分隔。序列均由大写字母组成。 
    输出
    第1行:一个整数L,表示两个序列的最长公共子序列的长度。 
    第2行:一个字符串S,表示两个序列的最长公共子序列。如果存在多个长度等于L的子序列,输出在X序列中位置最靠前的一个。 
    样例输入 Copy
    ABCBDAB BDCABA
    
    样例输出    Copy
    4
    BCBA
    View Code

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M = 100005;
    const int N = 1005;
    #define ri register int 
    char s1[N],s2[N];
    int n,m,dp[N][N];
    void out(int i,int j)
    {
        if(dp[i][j]==0) return ;
        while(dp[i][j]==dp[i-1][j]) i--;
        while(s1[i]!=s2[j]) j--;
        out(i-1,j-1);
        if(i<0) return ;
        printf("%c",s1[i]);
         
    }
    int d[N][N];
    int main(){
        scanf("%s%s",s1,s2);
        int len1=strlen(s1);
        int len2=strlen(s2);
        for(ri i=0;i<len1;i++)
        for(ri j=0;j<len2;j++)
        {
            if(s1[i]==s2[j])
            dp[i][j]=dp[i-1][j-1]+1;
            else
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
        printf("%d
    ",dp[len1-1][len2-1]);
        out(len1-1,len2-1);
    }
    View Code

     这道题就是仔细观察条件进行退 用 i 表示 到第几个 j 表示<的数量

    题目

    Problem 2 不等数列(num.cpp/c/pas)
    【题目描述】
    将1到n任意排列,然后在排列的每两个数之间根据他们的大小关系插入“>”和“<”。问在所有排列中,有多少个排列恰好有k个“<”。答案对2012取模。
    
    【输入格式】
    第一行2个整数n,k。
    
    【输出格式】
    一个整数表示答案。
    
    【样例输入】
    5 2
    【样例输出】
    66
    【数据范围】
    对于30%的数据:n <= 10
    对于100%的数据:k < n <= 1000
    View Code

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M = 10005;
    const int N = 10005;
    #define ri register int
    int f[1010][1010];
    int main(){
        int n,k;
        scanf("%d%d",&n,&k);
        f[2][0]=1;
        f[2][1]=1;
        for(ri i=3;i<=n;i++)
        for(ri j=0;j<=min(k,i-1);j++)
        {
            f[i][j]=(f[i][j]+f[i-1][j-1]*(i-j))%2015;
            f[i][j]=(f[i][j]+f[i-1][j]*(j+1))%2015;
        }
        printf("%d
    ",f[n][k]);
    }
    View Code

    逆向DP

       正着推有后向性时,就反看行不行。

     题目

     经营与开发
    
    时间限制: 1 Sec  内存限制: 128 MB
    
    题目描述
    
    4X概念体系,是指在PC战略游戏中一种相当普及和成熟的系统概念,得名自4个同样以“EX”为开头的英语单词。
    eXplore(探索)
    eXpand(拓张与发展)
    eXploit(经营与开发)
    eXterminate(征服)
    ——维基百科
    今次我们着重考虑exploit部分,并将其模型简化:
    你驾驶着一台带有钻头(初始能力值w)的飞船,按既定路线依次飞过n个星球。
    
    星球笼统的分为2类:资源型和维修型。(p为钻头当前能力值)
    1.资源型:含矿物质量a[i],若选择开采,则得到a[i]*p的金钱,之后钻头损耗k%,即p=p*(1-0.01k)
    2.维修型:维护费用b[i],若选择维修,则支付b[i]*p的金钱,之后钻头修复c%,即p=p*(1+0.01c)
        注:维修后钻头的能力值可以超过初始值(你可以认为是翻修+升级)
    请作为舰长的你仔细抉择以最大化收入。
     
    
    输入
    
    第一行4个整数n,k,c,w。
    以下n行,每行2个整数type,x。
    type为1则代表其为资源型星球,x为其矿物质含量a[i];
    type为2则代表其为维修型星球,x为其维护费用b[i];
     
    
    输出
    
    一个实数(保留2位小数),表示最大的收入。
     
    
    样例输入
    
    5 50 50 10
    1 10
    1 20
    2 10
    2 20
    1 30
     
    
    样例输出
    
    375.00
     
    
    提示
    
    对于30%的数据 n<=100
    另有20%的数据 n<=1000;k=100
    对于100%的数据 n<=100000; 0<=k,c,w,a[i],b[i]<=100;保证答案不超过10^9
    View Code

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M = 10005;
    const int N = 100005;
    #define ri register int 
    struct setdian{
        int leixing;
        double val;
    }p[N];
    int n;
    double ans,w,k,c;
    int main(){
        scanf("%d%lf%lf%lf",&n,&k,&c,&w);
        k=(1-0.01*k);
        c=(1+0.01*c);
        for(ri i=1;i<=n;i++)
        scanf("%d%lf",&p[i].leixing,&p[i].val);
        for(ri i=n;i>=1;i--)
        {
            if(p[i].leixing==1) ans=max(ans,ans*k+p[i].val);
            else ans=max(ans,ans*c-p[i].val);
        }
        printf("%.2lf
    ",w*ans);
    }
    View Code

     

        

  • 相关阅读:
    【HDU 1007】Quoit Design
    【BZOJ 4516】【SDOI 2016】生成魔咒
    【SPOJ 1812】Longest Common Substring II
    NOI2014 全国互测Round2
    1231: [Usaco2008 Nov]mixup2 混乱的奶牛
    3529: [Sdoi2014]数表
    2693: jzptab
    2565: 最长双回文串
    1562: [NOI2009]变换序列
    1965: [Ahoi2005]SHUFFLE 洗牌
  • 原文地址:https://www.cnblogs.com/Lamboofhome/p/11792622.html
Copyright © 2011-2022 走看看