hdu26道动态规划总结

前言:我们队的dp一直是我在做,说不上做的很顺,有些可以做,有些不能做。到现在为止,做dp题目也有七八十道了,除了背包问题的题目我可以说有百分之七八十的把握ac的话,其他类型的dp,还真没有多大把握。越是做dp题,就越是发现dp的博大精深,我想,dp这个专题,对于我这样的人来说,做上两百道,才能真正有所把握.........

25道dp题题目:

1.hdu  1503

题意:给你两个字符串,要你用这两个字符串组成这样一个字符串,在组成的字符串中字符的相对顺序不变的情况下,可以在组成的字符串中找到原先两个字符串,字母可以错开,但是相对顺序不能变化,要这个组成的字符串中字母数最少,并输出这个字符串。

Sample Input
apple peach
ananas banana
pear peach
 
Sample Output
appleach
bananas
pearch
 
思路:这是一道最长公共子序列的题目,当然还需要记录路径。把两个字符串的最长公共字串记录下来,在递归回溯输出的时候,要是两个字符是公共子串,就只输出一次,要不是,就分别把位于相同位置的两个字符串的字符输出.......
代码:
 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 int dp[1000][1000],vist[200][200];
 6 char s[1000],t[1000];
 7 void print(int x,int y)
 8 {
 9     if(x==0&&y==0)
10     return;
11     if(vist[x][y]==1)
12     {
13         print(x-1,y-1);
14         printf("%c",t[y-1]);
15     }
16     else if(vist[x][y]==2)
17     {
18         print(x-1,y);
19         printf("%c",s[x-1]);
20     }
21     else if(vist[x][y]==3)
22     {
23         print(x,y-1);
24         printf("%c",t[y-1]);
25     }
26     else if(vist[x][y]==0&&y==0&&x>0)
27     {
28         print(x-1,y);
29         printf("%c",s[x-1]);
30     }
31     else if(vist[x][y]==0&&x==0&&y>0)
32     {
33         print(x,y-1);
34         printf("%c",t[y-1]);
35     }
36 }
37 int main()
38 {
39     while(scanf("%s%s",s,t)>0)
40     {
41          int lens=strlen(s),lent=strlen(t);
42          for(int i=0;i<1000;i++)
43          dp[0][i]=dp[i][0]=0;
44          memset(vist,0,sizeof(vist));
45          for(int i=1;i<=lens;i++)
46          {
47                  for(int j=1;j<=lent;j++)
48                  if(s[i-1]==t[j-1])
49                  {
50                      dp[i][j]=dp[i-1][j-1]+1;
51                      vist[i][j]=1;
52                  }
53 
54                  else
55                  {
56                      int maxx=0;
57                      if(dp[i][j-1]<dp[i-1][j])
58                      {
59                          dp[i][j]=dp[i-1][j];
60                          vist[i][j]=2;
61                      }
62                      else
63                      {
64                          dp[i][j]=dp[i][j-1];
65                          vist[i][j]=3;
66                      }
67                  }
68          }
69          int i=lens,j=lent,cnt=0;
70          //printf("111
");
71          print(lens,lent);
72          printf("
");
73     }
74     return 0;
75 }
View Code

2、hdu 1502

题意:给你A,B,C三种字母,问你,当有n个A,n个B,n个C的时候,满足组成的字符串的所有前缀A的个数大于等于B的个数大于等于C的个数,并B的个数大于等于C的个数有多少个?

Sample Input
2
3
Sample Output
5
42
思路:这道dp题的转移方程是dp[i][j][k]=dp[i-1][j][k]+dp[i][j-1][k]+dp[i][j][k-1];(i>j>k)
当然,我是用记忆化搜索+打表过的,在记忆化搜索里面写大数的加法,然后打个表,就出结果了。
打表是一种神器,不要小看它........
记忆化搜索代码:
  1 #include<iostream>
  2 #include<stdio.h>
  3 #include<string.h>
  4 using namespace std;
  5 int dp[62][62][62][30],n;
  6 void dfs(int x,int y,int z,int w)
  7 {
  8     int i=0;
  9     while(dp[x][y][z][i]==0)  i++;
 10     if(i<=20)
 11     {
 12         //printf("%d   %d
",dp[x1][y1][z1][20],dp[x][y][z][20]);
 13         return;
 14     }
 15     if(x<y) return;
 16     if(x<z) return;
 17     if(y<z) return;
 18     if(x<n)
 19     {
 20         dfs(x+1,y,z,1);
 21         int i=0;
 22         while(dp[x+1][y][z][i]==0)  i++;
 23         if(i<=20)
 24         {
 25             //printf("%d %d %d
",x,y,z);
 26             for(int j=20;j>0;j--)
 27             {
 28                 dp[x][y][z][j]+=dp[x+1][y][z][j];
 29                 if(dp[x][y][z][j]/100000>0)
 30                 {
 31                     dp[x][y][z][j-1]+=dp[x][y][z][j]/100000;
 32                     dp[x][y][z][j]%=100000;
 33                 }
 34             }
 35         }
 36     }
 37 
 38     if(y<n)
 39     {
 40         dfs(x,y+1,z,2);
 41         int i=0;
 42         while(dp[x][y+1][z][i]==0)  i++;
 43         if(i<=20)
 44         {
 45             //printf("%d %d %d
",x,y,z);
 46             for(int j=20;j>0;j--)
 47             {
 48                 dp[x][y][z][j]+=dp[x][y+1][z][j];
 49                 if(dp[x][y][z][j]/100000>0)
 50                 {
 51                     dp[x][y][z][j-1]+=dp[x][y][z][j]/100000;
 52                     dp[x][y][z][j]%=100000;
 53                 }
 54             }
 55         }
 56     }
 57 
 58     if(z<n)
 59     {
 60         dfs(x,y,z+1,3);
 61         int i=0;
 62         while(dp[x][y][z+1][i]==0)  i++;
 63         if(i<=20)
 64         {
 65             //printf("%d %d %d
",x,y,z);
 66             for(int j=20;j>0;j--)
 67             {
 68                 dp[x][y][z][j]+=dp[x][y][z+1][j];
 69                 if(dp[x][y][z][j]/100000>0)
 70                 {
 71                     dp[x][y][z][j-1]+=dp[x][y][z][j]/100000;
 72                     dp[x][y][z][j]%=100000;
 73                 }
 74             }
 75         }
 76     }
 77 
 78 }
 79 int main()
 80 {
 81     //n=60;
 82     //memset(dp,0,sizeof(dp));
 83     //dp[60][60][60][20]=1;
 84     //dfs(0,0,0,0);
 85     n=0;
 86     while(scanf("%d",&n)>0)
 87     {
 88         int i=0;
 89         memset(dp,0,sizeof(dp));
 90         dp[n][n][n][20]=1;
 91         dfs(0,0,0,0);
 92         while(dp[0][0][0][i]==0)  i++;
 93         printf("%c",'"');
 94         printf("%d",dp[0][0][0][i]);
 95         i++;
 96         while(i<20)
 97         {
 98             printf("%05d",dp[0][0][0][i]);
 99             i++;
100         }
101         if(i==20)
102         {
103             printf("%05d",dp[0][0][0][i]);
104         }
105 
106         printf("%c",'"');
107         printf(",");
108         printf("
");
109         dp[n][n][n][20]=0;
110         n++;
111     }
112     return 0;
113 }
View Code

打表代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 char s[150][1000]={
 6 "1",
 7 "1",
 8 "5",
 9 "42",
10 "462",
11 "6006",
12 "87516",
13 "1385670",
14 "23371634",
15 "414315330",
16 "7646001090",
17 "145862174640",
18 "2861142656400",
19 "57468093927120",
20 "1178095925505960",
21 "24584089974896430",
22 "521086299271824330",
23 "11198784501894470250",
24 "243661974372798631650",
25 "5360563436201569896300",
26 "119115896614816702500900",
27 "2670926804331443293626900",
28 "60386171228363065768956000",
29 "1375596980582110638216817680",
30 "31554078431506568639711925552",
31 "728440733705121725605657358256",
32 "16916012593818937850175820875056",
33 "394984727560107218767652172156480",
34 "9269882950945137003216002357575872",
35 "218589820552932101591964442689934272",
36 "5177405669064206309480641678873685136",
37 "123139887106265725065261170839575261246",
38 "2940211742938376804365727956142799686970",
39 "70461309651358512358741033490151564263034",
40 "1694426732092192797198296281548882854896770",
41 "40879953049935966764838175153044218787509460",
42 "989318124094680800242093703952690318964293660",
43 "24011992526103689868224096174884123328708261100",
44 "584414956558400574946623386902564355477176447080",
45 "14261150342358043298392602404780869211095488665940",
46 "348876433985002864104580005170614922408018905657020",
47 "8555006509113973886896694412506009110609925390878620",
48 "210257823823361408953856390159370731312558948560177500",
49 "5178713915261459187808923452167773648813573133021584000",
50 "127816663734641521693312994768720558317819058630953008000",
51 "3160890723051037742300958639363743464856851891194511344000",
52 "78316111638147520232116305011469771592038383559489541704000",
53 "1943917771018304520047172570820410402016667020494472553010000",
54 "48334523581589010102952513742546024844918906756931542442556400",
55 "1203813957908516875152358489329058054078745007110871474716375280",
56 "30029983483935083858438698423851117882968874317657169412268673840",
57 "750270153399794678576435057573545926324276055884108148422050727840",
58 "18772482769028405636917719941593858764528793976890630506115671775200",
59 "470373947038907707302405010980987131831213397364392909428995307126880",
60 "11802109943885320655951253002795677125946808879324767545672973160638080",
61 "296516920131524804299707608337156053506400465189952712435084509896783040",
62 "7459203321130790040650176332416188852363369960068846727881499803410725440",
63 "187875141510304732204453155491218970539216498205240765481036372897711988800",
64 "4737637890492057297860769571861620074038072983555206964113320603342642320960",
65 "119605940186192921945993199027326146131452990076639651225155962772912609414400",
66 "3022912056752362939484322031260179006906680462576858197252183463144268821651200",
67 };
68 int main()
69 {
70     int n;
71     while(scanf("%d",&n)>0)
72     {
73         printf("%s

",s[n]);
74     }
75     return 0;
76 }
View Code

3、hdu3008

题意:需要打一只boss,小明和boss都是每秒攻击一次,但是是小明先攻击boss。小明和boss的血量都是100,小明的普通攻击为1,当然除了普通攻击外,小明还有魔法攻击,小明的魔法量也是100,每次魔法攻击都需要扣除一定量的魔法值,boss的攻击力是固定的为q,小明有n种魔法技能,在小明每次攻击之后会回复t点魔法值,每种魔法技能伤害为bi,耗费魔法值为ai,然后问小明打死boss所要用的最少时间,当然如果小明不能打死boss,输出My god

思路:dp[i][j]代表着小明第i次攻击,还剩下j点魔法值的情况下打掉的boss血量,

dp[i][j]=max(dp[i-1][j-t]+1,dp[i-1][j+s[k][0]-t]+s[k][1]);

当然,这只是一个粗略的转移方程,其中的魔法值不超过100,等等细节还需处理好

代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<algorithm>
 5 using namespace std;
 6 int dp[105][105];
 7 struct node
 8 {
 9     int x,y;
10 }s[105];
11 int cmp(const node a,const node b)
12 {
13     if((double)a.x/(double)a.y>(double)b.x/(double)b.y)
14     return 1;
15     else
16     return 0;
17 }
18 int main()
19 {
20     int n,p,t;
21     while(scanf("%d%d%d",&n,&p,&t)>0&&(n+p+t))
22     {
23         for(int i=1;i<=n;i++)
24         scanf("%d%d",&s[i].x,&s[i].y);
25         //sort(s+1,s+1+n,cmp);
26         memset(dp,0,sizeof(dp));
27         dp[0][0]=0;
28         s[0].x=0;               //把普通攻击也算成一种魔法攻击
29         s[0].y=1;
30         int flag=0;
31         int tmp=100/t;           //算出小明最多可以攻击的次数
32         if(100%t>0)
33         tmp++;
34         //printf("%d
",tmp);
35         int cnt=0;
36         for(int i=1;i<=tmp;i++)
37         {
38             for(int j=0;j<=n;j++)
39             {
40                 for(int k=100;k>=s[j].x;k--)
41                 {
42                     int ans=k-s[j].x+p;
43                     if(ans>100)
44                     ans=100;
45                     if(dp[i-1][k]+s[j].y>dp[i][ans])
46                     dp[i][ans]=dp[i-1][k]+s[j].y;
47                    // printf("%d %d  %d
",dp[i][ans],s[i].x,s[i].y);
48                     if(dp[i][ans]>=100)
49                     {
50                         flag=1;
51                         cnt=i;
52                         break;
53                     }
54                 }
55                 if(flag)
56                 break;
57             }
58             if(flag)
59             break;
60         }
61         if(flag==0)
62         printf("My god
");
63         else
64         printf("%d
",cnt);
65         //printf("%d
",dp[100][i]);
66     }
67     return 0;
68 }
View Code

4、hdu1501

题意:给你三个单词,在前两个单词相对顺序不变的情况下,是否能组成第三个单词?第三个单词的长度是前两个单词的长度和

思路:普通搜索和记忆化搜索都是可以的,主要是要剪枝,判断第三个单词的最后一个字母是否由前两个单词的最后一个字母组成......

代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 char s[4][500],dp[500];
 6 int len1,len2,len0,flag=0;
 7 int dfs(int num0,int num1,int num2)
 8 {
 9     //printf("1111
");
10     if(dp[num2]>0)
11     return 1;
12     if(num1>=len1&&num0>=len0)
13     return 0;
14     if(num0==len0&&s[1][num1]!=s[2][num2])
15     return 0;
16     if(num1==len1&&s[0][num0]!=s[2][num2])
17     return 0;
18     if(s[0][num0]!=s[2][num2]&&s[1][num1]!=s[2][num2])
19     return 0;
20     if(num0<len0&&s[0][num0]==s[2][num2])
21     {
22         dp[num2]=dfs(num0+1,num1,num2+1);
23     }
24     if(num1<len1&&s[1][num1]==s[2][num2])
25     {
26         dp[num2]=dfs(num0,num1+1,num2+1);
27     }
28     return dp[num2];
29 }
30 int main()
31 {
32     int text,f=0;
33     scanf("%d",&text);
34     while(text--)
35     {
36         scanf("%s%s%s",s[0],s[1],s[2]);
37         len0=strlen(s[0]);
38         len1=strlen(s[1]);
39         len2=strlen(s[2]);
40         flag=0;
41         memset(dp,0,sizeof(dp));
42         dp[len2]=1;
43         if(len0+len1>=len2&&(s[0][len0-1]==s[2][len2-1]||s[1][len1-1]==s[2][len2-1]))
44         flag=dfs(0,0,0);
45         printf("Data set %d: ",++f);
46         if(flag==1)
47         printf("yes
");
48         else
49         printf("no
");
50     }
51     return 0;
52 }
View Code

5、hdu1300

题意:有n种珠宝,每种珠宝有其等级,现在按照珠宝的等级从低到高给出,每种珠宝需要买的数量,以及珠宝的价格。每买一种珠宝,就必须多买额外10串这种珠宝,当然可以买高等级的珠宝来代替低等级的,这样就只需额外的买10串高等级的珠宝,求最少的价值。

思路:由于已经按照等级排好序,那么dp[i]代表买i种等级的珠宝所用的最少价值。那么dp[i]=min{dp[i-1]+(s[i][0]+10)*s[i][1],dp[j]+(sum[i]-sum[j]+10)*s[i][1]}(1<=k<i)

代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 int dp[100000],t[7],s[100000][2],cont[100000];
 6 int main()
 7 {
 8     int text;
 9     scanf("%d",&text);
10     while(text--)
11     {
12         int n;
13         scanf("%d",&n);
14         cont[0]=0;
15         for(int i=1;i<=n;i++)
16         {
17             scanf("%d%d",&s[i][0],&s[i][1]);
18             cont[i]=cont[i-1]+s[i][0];
19         }
20         dp[0]=0;
21         for(int i=1;i<=n;i++)
22         {
23             dp[i]=dp[i-1]+(s[i][0]+10)*s[i][1];
24             int minx=100000000;
25             for(int j=0;j<i;j++)
26             {
27                 int tmp=dp[j]+(cont[i]-cont[j]+10)*s[i][1];
28                 if(tmp<minx)
29                 minx=tmp;
30             }
31             if(dp[i]>minx)
32             dp[i]=minx;
33         }
34         printf("%d
",dp[n]);
35     }
36     return 0;
37 }
View Code

6、hdu1422(水题)

7、hdu1224

题意:给你n+1个点,其中1是起点,同时n+1是终点,终点和起点是同一个点,飞机只能从编号小的点飞到编号大的点,每个编号都有愉快值,求能从起点飞到终点的最大愉快值。

思路:dp[i]=max(dp[j])+s[i];(j<i)

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<queue>
 5 using namespace std;
 6 int dp[200],vist[200][200],a[200],path[200],n;
 7 void print(int num)
 8 {
 9     if(num==0)
10     return;
11     if(path[num]!=0)
12     {
13         print(path[num]);
14     }
15     if(num!=n+1)
16     printf("%d->",num);
17 }
18 int main()
19 {
20     int text,p=0;
21     scanf("%d",&text);
22     while(text--)
23     {
24         //int n;
25         scanf("%d",&n);
26         memset(a,0,sizeof(a));
27         for(int i=1;i<=n;i++)
28         scanf("%d",&a[i]);
29         int m;
30         scanf("%d",&m);
31         memset(vist,0,sizeof(vist));
32         memset(dp,0,sizeof(dp));
33         dp[1]=1;
34         for(int i=1;i<=m;i++)
35         {
36             int tmp1,tmp2;
37             scanf("%d%d",&tmp1,&tmp2);
38             if(tmp1>tmp2)
39             swap(tmp1,tmp2);
40             vist[tmp1][tmp2]=1;
41         }
42         memset(path,0,sizeof(path));
43         int maxn=1,k=-1;
44         for(int i=2;i<=n+1;i++)
45         {
46             int maxp=-10000;
47             for(int j=1;j<i;j++)
48             {
49                 if(dp[j]!=0&&vist[j][i]!=0&&maxp<dp[j])
50                 {
51                     maxp=dp[j];
52                     path[i]=j;
53                 }
54             }
55             if(maxp>dp[i])
56             dp[i]=maxp+a[i];
57             if(dp[i]>maxn)
58             {
59                 maxn=dp[i];
60                 k=i;
61             }
62         }
63         printf("CASE %d#
",++p);
64 
65         printf("points : %d
",dp[n+1]-1);
66 
67         printf("circuit : ");
68 
69         print(n+1);
70         printf("1
");
71         if(text)
72         printf("
");
73     }
74     return 0;
75 }
View Code

8、hdu1074
这是道状态压缩dp,我打算放在状态压缩dp里面再详细说说

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 struct node
 6 {
 7     int pre;
 8     int cost;
 9     int num;
10 }dp[42767];
11 struct node1
12 {
13     char name[205];
14     int cost;
15     int qx;
16 }s[20];
17 bool vist[50000];
18 void print(int x)
19 {
20     int tmp=x,k=0;
21     tmp=x^dp[x].pre;
22     tmp=tmp>>1;
23     //printf("%d
",tmp);
24     while(tmp)
25     {
26         k++;
27         tmp=tmp>>1;
28         //printf("%d
",tmp);
29     }
30     if(dp[x].pre!=0)
31     {
32         print(dp[x].pre);
33     }
34     printf("%s
",s[k].name);
35 }
36 int main()
37 {
38     int text;
39     scanf("%d",&text);
40     while(text--)
41     {
42         int n;
43         scanf("%d",&n);
44         for(int i=0;i<n;i++)
45         scanf("%s%d%d",s[i].name,&s[i].qx,&s[i].cost);
46         int m=1<<n;
47         dp[0].pre=-1;
48         dp[0].cost=0;
49         dp[0].num=0;
50         memset(vist,false,sizeof(vist));
51         vist[0]=true;
52         for(int i=0;i<m-1;i++)
53         {
54             for(int j=0;j<n;j++)
55             {
56                 int tmp=1<<j;
57                 if((tmp&i)==0)
58                 {
59                     int tmp1=tmp|i;
60                     dp[tmp1].cost=dp[i].cost+s[j].cost;
61                     int r=dp[tmp1].cost-s[j].qx;
62                     if(r<0)
63                     r=0;
64                     r=r+dp[i].num;
65                     if(!vist[tmp1])
66                     {
67                         vist[tmp1]=true;
68                         dp[tmp1].num=r;
69                         dp[tmp1].pre=i;
70                     }
71                     else if(dp[tmp1].num>r)
72                     {
73                         dp[tmp1].num=r;
74                         dp[tmp1].pre=i;
75                     }
76                 }
77             }
78         }
79         printf("%d
",dp[m-1].num);
80         print(m-1);
81     }
82     return 0;
83 }
View Code


9、hdu1080(带权值的最长公共子序列问题)

题意:给你两串字符,要你求这两字符的最大匹配值。每个字符与其他字符匹配,都有一个权值,权值表已经给出,输入输出中,只有这么几种字符,可以用空格代替字符。

思路:变形的最长公共子序列。

反思:我以前一直认为最长公共子序列就是哥模板,顶多再来个记录路径,没有想到,这次是它的变形。其实做题目,最好的就是做变形题,这样可以开拓自己的思路......

在最长公共子序列中,dp[i][j]代表的是字符串s前i-1个字符与字符串t钱j-1个字符的最大匹配数,所以,当s[i-1]与t[i-1]匹配的时候,取dp[i-1][j-1]+1;当s[i-1]与

t[j-1]不匹配的时候,我们取dp[i-1][j],dp[i][j-1]的最大值,dp[i-1][j]代表着s串中长度为i-2的字符与t串中长度为j-1的字符匹配的最大长度。

如此,这个题目带权值,那么

dp[i][j]代表的是,字符串s前i-1个字符与字符串t钱j-1个字符匹配的最大权值,那么当我们让s[i-1]与t[j-1]匹配的时候(这两个字符不一定要相等,只是需要匹配的权值),可以匹配上的话,dp[i][j]=dp[i-1][j-1]+匹配上的权值;

若是不能匹配的话,dp[i][j]有两种情况,dp[i][j]=dp[i-1][j]+s串中第i-1个字符与t串中空格所匹配的权值........下面那个也是同理

代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 #define M -1000000000
 6 int dp[200][200];
 7 char s[200],t[200];
 8 int find(char ch)
 9 {
10     if(ch=='A')
11     return 0;
12     if(ch=='C')
13     return 1;
14     if(ch=='G')
15     return 2;
16     if(ch=='T')
17     return 3;
18     if(ch=='-')
19     return 4;
20 }
21 int main()
22 {
23     int text;
24     scanf("%d",&text);
25     int a[5][5]={
26                      5,-1,-2,-1,-3,
27                      -1,5,-3,-2,-4,
28                      -2,-3,5,-2,-2,
29                      -1,-2,-2,5,-1,
30                      -3,-4,-2,-1,M,
31                 };
32     while(text--)
33     {
34         int lens,lent;
35         scanf("%d %s",&lens,s);
36         scanf("%d %s",&lent,t);
37         dp[0][0]=0;
38         for(int i=1;i<=lens;i++)
39         dp[i][0]=a[find(s[i-1])][find('-')]+dp[i-1][0];
40         for(int i=1;i<=lent;i++)
41         dp[0][i]=a[find('-')][find(t[i-1])]+dp[0][i-1];
42         for(int i=1;i<=lens;i++)
43         {
44             for(int j=1;j<=lent;j++)
45             {
46                 dp[i][j]=dp[i-1][j-1]+a[find(s[i-1])][find(t[j-1])];
47                 dp[i][j]=max(dp[i][j],dp[i][j-1]+a[find('-')][find(t[j-1])]);
48                 dp[i][j]=max(dp[i][j],dp[i-1][j]+a[find(s[i-1])][find('-')]);
49             }
50         }
51         printf("%d
",dp[lens][lent]);
52     }
53     return 0;
54 }
View Code

 10、hdu1059(背包问题,在背包总结有详细解答)

11、hdu1158

题意:经理有三种操作,雇佣一个人,支付它工资,开除一个人,这三种操作的花费分别为a,b,c,然后每个月经理需要si个人做事,问n个月后,经理的最小花费。

思路:dp[i][j]代表第i天有j个人时的最小花费。

dp[i][j]=min(dp[i-1][k]+abs(j-k)*(a (or) c)+j*b)(s[i-1]<=k<=maxn);其中,maxn为要雇佣的最大人数

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 int dp[13][10100],s[13];
 6 int main()
 7 {
 8     int n;
 9     while(scanf("%d",&n)>0&&n)
10     {
11         int a,b,c,maxn=0;
12         scanf("%d%d%d",&a,&b,&c);
13         for(int i=1;i<=n;i++)
14         {
15             scanf("%d",&s[i]);
16             if(maxn<s[i])
17             maxn=s[i];
18         }
19         for(int i=1;i<=maxn;i++)      //第一天的花费是可以确定的
20         dp[1][i]=i*(a+b);
21         for(int i=2;i<=n;i++)
22         {
23             for(int j=s[i];j<=maxn;j++)
24             {
25                 dp[i][j]=1<<30;            //赋值为最大
26                 for(int k=s[i-1];k<=maxn;k++)
27                 {
28                     int cost;
29                     if(k<j)
30                     cost=j*b+(j-k)*a;
31                     else
32                     cost=j*b+(k-j)*c;
33                     dp[i][j]=min(dp[i][j],dp[i-1][k]+cost);
34                 }
35             }
36         }
37         int minx=dp[n][maxn];
38         for(int i=s[n];i<=maxn;i++)
39         if(minx>dp[n][i])
40         minx=dp[n][i];
41         printf("%d
",minx);
42     }
43     return 0;
44 }
View Code


12、hdu2059(水)

13、hdu1081(最长递增子序列变形)

题意:给你一些带权值的点组成的矩形,要你在这个矩形内找一个子矩形,使得这个子矩形内的点加起来权值最大......

As an example, the maximal sub-rectangle of the array:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
is in the lower left corner:
9 2
-4 1
-1 8

思路:这其实就是一个二维的最长递增子序列......我们只需要把二维压缩成以为就好......

代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 int s[200][200],t[200];
 6 int main()
 7 {
 8     int n;
 9     while(scanf("%d",&n)>0)
10     {
11         for(int i=1;i<=n;i++)
12         {
13             for(int j=1;j<=n;j++)
14             scanf("%d",&s[i][j]);
15         }
16         int maxx=-100000000,sum;
17         for(int i=1;i<=n;i++)
18         {
19             memset(t,0,sizeof(t));
20             for(int j=i;j<=n;j++)
21             {
22                 for(int k=1;k<=n;k++)
23                 {
24                     t[k]+=s[j][k];
25                 }
26                 sum=0;
27                 for(int p=1;p<=n;p++)
28                 {
29                     sum+=t[p];
30                     if(sum<0)
31                     sum=0;
32                     if(sum>maxx)
33                     maxx=sum;
34                 }
35                 if(maxx<sum)
36                 maxx=sum;
37             }
38         }
39         printf("%d
",maxx);
40     }
41     return 0;
42 }
View Code


14、hdu1078(水)

解题报告:http://www.cnblogs.com/ziyi--caolu/p/3197714.html

15、hdu1025(最长递增子序列模板?.....忘了,反正比较水)

代码O(nlongn):

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<stdio.h>
 4 #include<algorithm>
 5 using namespace std;
 6 struct node
 7 {
 8     int a,b;
 9 }s[500005];
10 int cmp(const node p,const node q)
11 {
12     if(p.a<q.a)
13     return 1;
14     else if(p.a==q.a&&p.b<q.b)
15     return 1;
16     else
17     return 0;
18 }
19 int dp[500005];
20 int main()
21 {
22     int n;
23     int text=0;
24     //scanf("%d",&text);
25     while(scanf("%d",&n)>0)
26     {
27         
28         for(int i=0;i<n;i++)
29         scanf("%d%d",&s[i].a,&s[i].b);
30         sort(s,s+n,cmp);
31         dp[0]=s[0].b;
32         int len=1;
33         for(int i=1;i<n;i++)
34         {
35             int left=0,right=len-1,mid;
36             if(dp[len-1]<s[i].b)
37             dp[len++]=s[i].b;
38             else
39             {
40                 right=len-1;
41                 while(left<=right)
42                 {
43                     mid=(left+right)/2;
44                     if(dp[mid]<s[i].b)
45                     left=mid+1;
46                     else  right=mid-1;
47                 }
48                 dp[left]=s[i].b;
49             }
50         }
51         printf("Case %d:
",++text);
52         if(len==1)
53         printf("My king, at most %d road can be built.

",len);
54         else
55         printf("My king, at most %d roads can be built.

",len);
56     }
57     return 0;
58 } 
View Code

16、hdu1160(赤裸裸的最长递增子序列,模板题)

17、hdu1024(很明显的最大m字段和问题)

题意:给你一个m,一个n,后面跟n个数,要你把这n个数分成m段,m段加起来的和最大.....求最大和

思路:这个题目应当算作区间dp的,只是看到了,就将它ac了吧。dp[i][j]表示将前i个数分为j段的最大和

那么dp[i][j]=max{dp[i-1][j]+a[i],max(dp[k][j-1])+a[i]}其中k>=1,k<i;

再开一个数组来记录max(dp[k][j-1])就ok.......当然,这个题目还需要将二维压缩成一维.....

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 typedef __int64 ss;
 6 #define minx -100000000
 7 ss dp[1000005],f[1000005],a[1000005];
 8 int main()
 9 {
10     ss n,m;
11     while(scanf("%I64d%I64d",&m,&n)>0)
12     {
13         for(int i=1;i<=n;i++)
14         {
15             scanf("%I64d",&a[i]);
16             dp[i]=f[i]=0;
17         }
18         dp[0]=f[0]=0;
19         ss maxn;
20         for(int i=1;i<=m;i++)
21         {
22             maxn=minx;
23             for(int j=i;j<=n;j++)
24             {
25                 dp[j]=max(dp[j-1],f[j-1])+a[j];
26                 f[j-1]=maxn;
27                 if(maxn<dp[j])
28                 maxn=dp[j];
29             }
30         }
31         printf("%I64d
",maxn);
32     }
33     return 0;
34 }
View Code


18、hdu1114(背包水题)

19、hdu2191(背包水题)

20、hdu1978(记忆化搜索水题)

21、hdu1789(贪心)

22、hdu1058(经典dp)

题意:

A number whose only prime factors are 2,3,5 or 7 is called a humble number. The sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 25, 27, ... shows the first 20 humble numbers.
Write a program to find and print the nth element in this sequence
思路:它的经典在于用n个变量去处理多种结果,这个在我以前的思维中是没有的
代码:
 1 #include<iostream>
 2 using namespace std;
 3 typedef __int64 ss;
 4 int min(int x,int y,int j,int k)
 5 {
 6     int min1,min2;
 7     if(x>y)
 8         min1=y;
 9     else
10         min1=x;
11     if(j>k)
12         min2=k;
13     else
14         min2=j;
15     return (min1>min2? min2:min1);
16 }
17 int main()
18 {
19     ss dp[6000],j,m,n,k,i;
20     dp[1]=1;
21     j=m=n=k=1;
22     i=2;
23     while(i<5843)
24     {
25         dp[i]=min(dp[j]*2,dp[m]*3,dp[n]*5,dp[k]*7);
26         if(dp[i]==dp[j]*2)
27             j++;
28         if(dp[i]==dp[m]*3)
29             m++;
30         if(dp[i]==dp[n]*5)
31             n++;
32         if(dp[i]==dp[k]*7)
33             k++;
34         i++;
35     }
36     while(scanf("%I64d",&n)>0&&n)
37     {
38         ss r=n%10;
39         ss f=n%100;
40         if(r==1&&f!=11)
41             printf("The %I64dst humble number is %I64d.
",n,dp[n]);
42         else if(r==2&&f!=12)
43             printf("The %I64dnd humble number is %I64d.
",n,dp[n]);
44         else if(r==3&&f!=13)
45             printf("The %I64drd humble number is %I64d.
",n,dp[n]);
46         else 
47             printf("The %I64dth humble number is %I64d.
",n,dp[n]);
48     }
49     return 0;
50 }
View Code


23、hdu1421

题意:

搬寝室是很累的,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件物品后的最佳状态是怎样的(也就是最低的疲劳度),请告诉他吧.
 
思路:对物品的重量排序之后,dp[i][j]代表在i件物品中拿j对物品的最小疲劳度
dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1]));
代码:
 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<algorithm>
 5 using namespace std;
 6 int dp[2005][2000],a[2005];
 7 int main()
 8 {
 9     int n,k;
10     while(scanf("%d%d",&n,&k)>0)
11     {
12         for(int i=1;i<=n;i++)
13         scanf("%d",&a[i]);
14         sort(a+1,a+1+n);
15         for(int i=1;i<=n;i++)
16         for(int j=1;j<=k;j++)
17         dp[i][j]=1000000000;
18         for(int i=1;i<=n;i++)
19         dp[i][0]=0;
20         for(int i=2;i<=n;i++)
21         {
22             for(int j=1;j<=i/2&&j<=k;j++)
23             dp[i][j]=min(dp[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1]),dp[i-1][j]);
24         }
25         printf("%d
",dp[n][k]);
26     }
27     return 0;
28 }
View Code

24、hdu1159(最长公共子序列模板题)

25、hdu1257(贪心或最长递增子序列)

26、hdu1244

题意:

给定一个由n个正整数组成的整数序列
a1 a2 a3 ... an
求按先后次序在其中取m段长度分别为l1、l2、l3...lm的不交叠的连续整数的和的最大值。
 
思路:dp[i][j]表示在前i个元素中取j段的和的最大值。那么dp[i][j]=max{dp[i-1][j],dp[i-a[j]][j-1]+sum[i]-sum[i-a[j]]};
代码:
 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 int dp[1005][25],a[1005],sum[1005],s[25];
 6 int main()
 7 {
 8     int n;
 9     while(scanf("%d",&n)>0&&n)
10     {
11         int m;
12         scanf("%d",&m);
13         for(int i=1;i<=m;i++)
14         scanf("%d",&s[i]);
15         memset(dp,0,sizeof(dp));
16         sum[0]=0;
17         for(int i=1;i<=n;i++)
18         {
19             scanf("%d",&a[i]);
20             sum[i]=sum[i-1]+a[i];
21         }
22         int ans=0;
23         for(int j=1;j<=m;j++)
24         {
25             ans+=s[j];
26             for(int i=ans;i<=n;i++)
27             {
28                 dp[i][j]=max(dp[i-1][j],dp[i-s[j]][j-1]+sum[i]-sum[i-s[j]]);
29             }
30         }
31         printf("%d
",dp[n][m]);
32     }
33     return 0;
34 }
View Code
原文地址:https://www.cnblogs.com/ziyi--caolu/p/3283175.html