回顾一些较简单的dp题

1.导弹拦截  (+贪心)

  两问:一个导弹拦截系统最多能拦多少导弹 要拦截所有导弹至少需要多少拦截系统

  第一问感觉是一个比较巧妙的方法:

    维护一个单调递减的序列 length[] 记录的是拦截导弹的高度

    当下一个导弹小于 length[] 最后一个数(最小的数)则直接把它加在序列后即可

    若大于 则找到序列中比它大的最小的数(二分)然后替换 可以保证最优

  第二问 就是贪心啊

    当现有的导弹系统的拦截高度都小于当前导弹的高度 则开一个新的系统

    否则找到拦截高度比导弹高度高的最小的系统来拦截

    这里记录系统拦截高度的数组一定是单调递增的无需排序

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int x;
bool f;
int height[100010],length[100010],minh[100010];
int main()
{
    int y=1;
    while(scanf("%d",&height[y])==1) y++;y--;
    length[1]=height[1];
    int top=1;
    for(int i=2;i<=y;i++)
    {
        if(height[i]<=length[top]) length[++top]=height[i];
        else
        {
            int left=1,right=top,ans;
            while(left<=right)
            {
                int mid=(left+right)/2;
                if(length[mid]<height[i])
                {
                    ans=mid;
                    right=mid-1;
                }
                else left=mid+1;
            }
            length[ans]=height[i];
        }
    }
    cout<<top<<endl;
    int maxa=0;
    for(int i=1;i<=y;i++)
    if(maxa<length[i]) maxa=length[i];
    int num=1;minh[1]=height[1];
    for(int i=2;i<=y;i++)
    {
        f=0;
        for(int j=1;j<=num;j++)
         if(height[i]<=minh[j]) {minh[j]=height[i];f=1;break;}
        if(f==0) minh[++num]=height[i];
    }
    cout<<num;
    return 0;
}

2.石子合并 (+断环为链法)

  区间dp一般套路:枚举起点 枚举区间长度(或终点) 枚举断点 转移

  注意枚举的顺序要保证由已知推未知

  很多时候遇到环都可能要断环为链(图论也是这样)

 1 #include<iostream>
 2 using namespace std;
 3 int a[210];
 4 int s[210];
 5 int ans1[210][210];
 6 int ans2[210][210];
 7 int main()
 8 {
 9     int n;
10     cin>>n;
11     for(int i=1;i<=n;i++)
12     {
13         cin>>a[i];
14         s[i]=s[i-1]+a[i];
15         a[i+n]=a[i];
16     }
17     for(int i=n+1;i<=n*2;i++)
18     s[i]=s[i-1]+a[i];
19     for(int i=1;i<=2*n;i++)
20     for(int j=1;j<=2*n;j++)
21     if(j!=i) ans2[i][j]=2100000;
22     for(int h=1;h<=n;h++)
23     {
24         for(int i=h+n-1;i>=h;i--)
25         {
26             for(int j=i;j<=h+n-1;j++)
27             {
28                 for(int k=i+1;k<=j;k++)
29                 {
30                     ans1[i][j]=max(ans1[i][j],ans1[i][k-1]+ans1[k][j]+s[j]-s[i-1]);
31                     ans2[i][j]=min(ans2[i][j],ans2[i][k-1]+ans2[k][j]+s[j]-s[i-1]);
32                 }
33             }
34         }
35     }
36     int maxn,minn=2100000;
37     for(int i=1;i<=n;i++)
38     {
39         maxn=max(maxn,ans1[i][i+n-1]);
40         minn=min(minn,ans2[i][i+n-1]);
41     }
42     cout<<minn<<endl<<maxn<<endl;
43     return 0;
44 } 

3.相似基因

  现在看起来好像很显然的样子

  f[i][j] 表示第一个字符串匹配到第i位 第二个字符串匹配到第j位最大的相似度

  三种情况转移 分别是在第一个,第二个字符串加空碱基 不加空碱基

  注意初始化

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int len1,len2,sum;
 5 char s1[110],s2[110];
 6 int a1[110],a2[110];
 7 char ch;
 8 int f[110][110];
 9 int s[6][6]={{0,0,0,0,0,0},
10              {0,5,-1,-2,-1,-3},
11              {0,-1,5,-3,-2,-4},
12              {0,-2,-3,5,-2,-2},
13              {0,-1,-2,-2,5,-1},
14              {0,-3,-4,-2,-1,0}};
15 int change(char x)
16 {
17     if(x==‘A‘) return 1;
18     else if(x==‘C‘) return 2;
19     else if(x==‘G‘) return 3;
20     else if(x==‘T‘) return 4;
21 }
22 int main()
23 {
24     scanf("%d",&len1);ch=getchar();
25     for(int i=1;i<=len1;i++) scanf("%c",&s1[i]);
26     scanf("%d",&len2);ch=getchar();
27     for(int i=1;i<=len2;i++) scanf("%c",&s2[i]);
28     for(int i=1;i<=len1;i++) a1[i]=change(s1[i]);
29     for(int i=1;i<=len2;i++) a2[i]=change(s2[i]);
30     for(int i=1;i<=len1;i++)
31         for(int j=1;j<=len2;j++) f[i][j]=-21000000;
32     for(int i=1;i<=len1;i++) f[i][0]=f[i-1][0]+s[a1[i]][5];
33     for(int i=1;i<=len2;i++) f[0][i]=f[0][i-1]+s[a2[i]][5];
34     for(int i=1;i<=len1;i++)
35     {
36         for(int j=1;j<=len2;j++)
37         {
38             f[i][j]=max(f[i][j],f[i-1][j]+s[a1[i]][5]);
39             f[i][j]=max(f[i][j],f[i][j-1]+s[a2[j]][5]);
40             f[i][j]=max(f[i][j],f[i-1][j-1]+s[a1[i]][a2[j]]);
41         }
42     }
43     printf("%d",f[len1][len2]);
44     return 0;
45 }

4.传纸条(优化维度)

  四维dp 优化成三维

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 int read()
 5 {
 6     int ans=0;char c;
 7     c=getchar();
 8     while(c<‘0‘||c>‘9‘) c=getchar();
 9     while(c>=‘0‘&&c<=‘9‘) {ans=ans*10+c-‘0‘;c=getchar();}
10     return ans;
11 }
12 int f[1010][510][510];
13 int a[510][510];
14 int main()
15 {
16     int m=read(),n=read();
17     for(int i=1;i<=m;i++)
18     for(int j=1;j<=n;j++)
19     a[i][j]=read();
20     for(int k=1;k<=n+m;k++)
21     {
22         for(int i=1;i<=n;i++)
23         {
24             for(int j=1;j<=n;j++)
25             {
26                 if(k-i+1<1||k-j+1<1) continue;
27                 f[k][i][j]=max(max(max(f[k-1][i][j],f[k-1][i-1][j]),f[k-1][i][j-1]),f[k-1][i-1][j-1]);
28                 f[k][i][j]+=a[k-i][i]+a[k-j][j];
29                 if(i==j) f[k][i][j]-=a[k-i][i];
30             }
31         }
32     }
33     cout<<f[m+n][n][n];
34     return 0;
35 } 

5.花店橱窗布置

  这一题要输出最优方案 所以f[i][j]表示的是前i束花放在前j个瓶子里 且第i束花放在第j个瓶子里的最大美学值

 1 #include<iostream>
 2 using namespace std;
 3 int a[110][110];  //a[i][j] 第i束花放在第j个花瓶中的美学值
 4 int b[110][110];  //b[i][j] 前i束花放在前j个花瓶中的最大美学值
 5 int c[110][110],d[110];
 6 int main()
 7 {
 8     int f,v;
 9     cin>>f>>v;
10     for(int i=1;i<=f;i++)
11     for(int j=1;j<=v;j++)
12     cin>>a[i][j];
13     //for(int i=1;i<=v-f+1;i++) b[1][i]=a[1][i];
14     for(int i=1;i<=f;i++)
15     for(int j=1;j<=v;j++)
16     b[i][j]=-2100000000;
17     /*如果b数组中初始值都为0
18     那么当第一束花放在前几个花瓶中美学值为负数时就会出错;
19     或:直接初始化第一束花放在前几个花瓶中的美学值(前面被注释掉的)
20     但注意此时后面一个循环的i从2开始*/
21
22     for(int i=1;i<=f;i++)
23     for(int j=i;j<=v-f+i;j++) //j<=v-f+i!!!
24     for(int k=i-1;k<=j-1;k++)
25     {
26         if(b[i][j]<b[i-1][k]+a[i][j])
27         {
28             b[i][j]=b[i-1][k]+a[i][j];
29             c[i][j]=k;
30         }
31     }
32     int maxn=-2100000000,k;
33     for(int i=f;i<=v;i++)
34     {
35         if(b[f][i]>maxn)
36         {
37             maxn=b[f][i];
38             k=i;
39         }
40     }
41     cout<<maxn<<endl;
42     for(int i=1;i<=f;i++)
43     {
44         d[i]=k;
45         k=c[f-i+1][k];
46     }
47     for(int j=f;j>=1;j--) cout<<d[j]<<" ";
48     return 0;
49 }

6.编辑距离

  和3类似

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 char a[2010],b[2010];
 6 int f[2010][2010];
 7 int main()
 8 {
 9     int m,n,i,j;
10     scanf("%s%s",a,b);
11     m=strlen(a);n=strlen(b);
12     for(int i=m+1;i>=1;i--) a[i]=a[i-1];
13     for(int j=n+1;j>=1;j--) b[j]=b[j-1];
14     for(i=1;i<=m;i++) f[i][0]=i;
15     for(j=1;j<=n;j++) f[0][j]=j;
16     for(i=1;i<=m;i++)
17     {
18         for(j=1;j<=n;j++)
19         {
20             if(a[i]==b[j]) f[i][j]=f[i-1][j-1];
21             else
22             {
23                 f[i][j]=min(min(f[i-1][j-1],f[i-1][j]),f[i][j-1])+1;
24             }
25         }
26     }
27     cout<<f[m][n];
28     return 0;
29 }

7.乘积最大

  f[i][j] 表示到第i位数已经加了j个乘号的最大乘积

  加高精有点麻烦啊

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<string>
 5 #define rg register
 6 #define M 110
 7 using namespace std;
 8 int read()
 9 {
10   int x=0,y=1;char c;
11   c=getchar();
12   while(c<‘0‘||c>‘9‘) {if(c==‘-‘) y=-1;c=getchar();}
13   while(c>=‘0‘&&c<=‘9‘) {x=(x<<1)+(x<<3)+c-‘0‘;c=getchar();}
14   return x*y;
15 }
16 string ss;
17 int n1,k1;
18 int a[M];
19 struct node
20 {
21   int s[M];
22   int len;
23 }f[10][M];
24 node calc(node x,int l,int r)
25 {
26   node x1,y1;y1.len=r-l+1;
27   int len1=x.len,len2=y1.len,len=len1+len2-1;
28   memset(x1.s,0,sizeof(x1.s));
29   memset(y1.s,0,sizeof(y1.s));
30   for(rg int i=r;i>=l;i--) y1.s[r-i+1]=a[i];
31   for(rg int i=1;i<=len1;i++)
32     for(rg int j=1;j<=len2;j++)
33       x1.s[i+j-1]+=x.s[i]*y1.s[j];
34   for(rg int i=1;i<=len;i++)
35     {
36       x1.s[i+1]+=x1.s[i]/10;
37       x1.s[i]%=10;
38     }
39   if(x1.s[len+1]) len++;
40   x1.len=len;
41   return x1;
42 }
43 node cmp(node x,node y)
44 {
45   int len1=x.len,len2=y.len;
46   if(len1<len2) return y;
47   if(len1>len2) return x;
48   for(rg int i=len1;i>=1;i--)
49     {
50       if(x.s[i]>y.s[i]) return x;
51       if(x.s[i]<y.s[i]) return y;
52     }
53   return y;
54 }
55 int main()
56 {
57   n1=read();k1=read();
58   cin>>ss;
59   for(int i=1;i<=n1;i++) a[i]=ss[i-1]-‘0‘;
60   for(int i=1;i<=n1;i++)
61     for(int j=i;j>=1;j--)
62       f[0][i].s[++f[0][i].len]=a[j];
63   for(int i=2;i<=n1;i++) //前i个数
64     {
65       int maxn=min(i-1,k1);
66       for(int k=1;k<=maxn;k++)//k个乘号
67       for(int j=k;j<i;j++)//第k个乘号放哪
68       f[k][i]=cmp(f[k][i],calc(f[k-1][j],j+1,i));
69     }
70    for(int i=f[k1][n1].len;i>=1;i--)
71     printf("%d",f[k1][n1].s[i]);
72       return 0;
73 }

原文地址:https://www.cnblogs.com/forward777/p/10336200.html

时间: 2024-10-18 15:57:45

回顾一些较简单的dp题的相关文章

从一道简单的dp题中学到的...

今天想学点动态规划的知识,于是就看了杭电的课件,数塔问题啊,LCS啊都是比较经典的动规了,然后随便看了看就开始做课后练习题... HDOJ 1421 搬寝室 http://acm.hdu.edu.cn/showproblem.php?pid=1421 题目大意:从n(n <= 2000)个数中选出k对数(即2*k个),使它们的差的平方和最小. 例如:从8,1,10,9,9中选出2对数,要使差的平方和最小,则应该选8和9.9和10,这样最小,结果为2 因为知道是dp的题,先建个dp[][]数组,然

简单的dp hdu 数塔(水题)

数塔 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 21314    Accepted Submission(s): 12808 Problem Description 在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的: 有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少

2014 HDU多校弟九场I题 不会DP也能水出来的简单DP题

听了ZWK大大的思路,就立马1A了 思路是这样的: 算最小GPA的时候,首先每个科目分配到69分(不足的话直接输出GPA 2),然后FOR循环下来使REMAIN POINT减少,每个科目的上限加到100即可 算最大GPA的时候,首先每个科目分配到60分,然后FOR循环下来使REMAIN POINT减少,每个科目的上限加到85即可,如果还有REMAIN POINT,就FOR循环下来加到100上限即可 不会DP 阿 QAQ 过段时间得好好看DP了  =  = 于是默默的把这题标记为<水题集> //

ACM/ICPC算法训练 之 数学很重要-浅谈“排列计数” (DP题-POJ1037)

这一题是最近在看Coursera的<算法与设计>的公开课时看到的一道较难的DP例题,之所以写下来,一方面是因为DP的状态我想了很久才想明白,所以借此记录,另一方面是看到这一题有运用到 排列计数 的方法,虽然排列计数的思路简单,但却是算法中一个数学优化的点睛之笔. Poj1037  A decorative fence 题意:有K组数据(1~100),每组数据给出总木棒数N(1~20)和一个排列数C(64位整型范围内),N个木棒长度各异,按照以下条件排列,并将所有可能结果进行字典序排序 1.每一

4815 江哥的dp题a

4815 江哥的dp题a 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 给出一个长度为N的序列A(A1,A2,A3,...,AN).现选择K个互不相同的元素,要求: 1.两两元素互不相邻 2.元素值之和最大 输入描述 Input Description 第一行两个正整数N,K. 接下来一行N个整数,描述A. 输出描述 Output Description 输出一行一个整数,描述答案(最大和). 样例输入 Sample I

poj2955 Brackets 简单区间dp

// poj2955 简单区间dp // d[i][j]表示i到j区间所能形成的最大匹配序列 // dp[i][j] = max(dp[i][k]+dp[k+1][j]){i<k<j} // dp[i][j] = max(dp[i+1][j-1]+2) if (s[i] match s[j]) // // 记忆化搜索的时候,将dp[i][i] = 0 ,其他赋值成-1; // // 做题的时候刚开始将dp[i][j]弄成0了,结果一直tle // 最后发现有好多的状态重复计算了,所以会tle

简单概率DP——hdu4405

题目描述: Hzz loves aeroplane chess very much. The chess map contains N+1 grids labeled from 0 to N. Hzz starts at grid 0. For each step he throws a dice(a dice have six faces with equal probability to face up and the numbers on the faces are 1,2,3,4,5,6

杭电dp题集,附链接

Robberies 点击打开链接 背包;第一次做的时候把概率当做背包(放大100000倍化为整数):在此范围内最多能抢多少钱  最脑残的是把总的概率以为是抢N家银行的概率之和- 把状态转移方程写成了f[j]=max{f[j],f[j-q[i].v]+q[i].money}(f[j]表示在概率j之下能抢的大洋); 正确的方程是:f[j]=max(f[j],f[j-q[i].money]*q[i].v)  其中,f[j]表示抢j块大洋的最大的逃脱概率,条件是f[j-q[i].money]可达,也就是

csu 2014 summer training day 3 简单树形dp

FZU 2157 题意:树上的节点可以打上0或1的标记,树的权值由两部分呢组成,点权和边权,有00.01.10.11四种组合的边权, 问最小权值和.以1节点为树根 分析:dp[x][0]表示x标记0后的最小的权值,dp[x][1]同理 那么每次可以计算dp[x][0],dp[x][1]: 例如dp[x][1]=min(dp[son][0]+lab[0][1]+val[1],dp[son][1]+bal[1][1]+val[1]); 具体看代码. 用bfs写的分层,深搜代码更简洁 1 #inclu