动态规划·题型归纳

背包

1.填满型01背包[1]

   倒序枚举避开后效性 

 

题目描述

caioj1055【题意】
    有n根木棍(0≤n≤30),从中选若干根使得它们的 长度和s  最接近v(正整数,0≤v≤20000),且s<=v。
【输入格式】   
    一个整数v,一个整数n。接下来n个整数,分别表示这n根木棍的长度。  
【输出格式】
    一个整数,表示v-s。
【样例输入】
24
6
8 3 12 7 9 7
【样例输出】
0

 

#include<bits/stdc++.h>
using namespace std;
int a[350],v,n;
bool f[20000];
int main()
{
   cin>>v>>n;
   for(int i=1;i<=n;i++)cin>>a[i];
   f[0]=1;
   for(int i=1;i<=n;i++)
    for(int j=v;j>=a[i];j--)
     if(!f[j])f[j]=f[j-a[i]];
     
   int x=v;
   while(!f[x])x--;
   cout<<v-x;
   return 0;
}

 2.填满型01背包 [2]

搞了一个小时才弄出来的东西…emmm和上一个差不多

 

caioj1056【题意】
    有n个数列,每个数列各自选若干个数,使得每个数列的和一样大,并且这个和要尽量大。
【输入格式】
    第一行是一个整数N(N<=100),表示一共有n个数列。
    以下N行每行是一个系列非负整数,表示每个数列的数字,用-1结束。
    一个数列中的数字个数不超过100个,每个数也不超过100。
【输出格式】
    一个整数,表示使得每个数列的和一样大,并且这个和要尽量大的值。如果找不到合适的方案,则输出0。
【样例输入】
2
2 1 -1
3 2 1 -1
【样例输出】
3

#include<bits/stdc++.h>
using namespace std;
const int N = 1100;
int n,x,maxn,a[N],tot[N];
bool f[N];  
int main()
{
   scanf("%d",&n);
   
   for(int i=1;i<=n;i++){
   int len=0;
   while(scanf("%d",&x)!=EOF){
       if(x==-1)break;
       a[++len]=x;
   }
   maxn=0;  //千万不要忘记清0! 
   f[0]=1; 
   //以下是核心部分!也是倒序看看能更新哪些数
   for(int j=1;j<=len;j++)
    for(int k=maxn;k>=0;k--){ //核心·卡了一个小时的k循环!=。=
        if(f[k]){
            f[k+a[j]]=1;
            maxn=max(maxn,k+a[j]);
        }
    }  //这个循环应该可以优化……
    
    for(int z=maxn;z>=0;z--){
        if(f[z])tot[z]++;
         }
    } 
    for(int i=maxn;i>=0;i--){
        if(tot[i]==n){
            cout<<i;
            break;
        }
    } 
}

 

 

 

3.含价值的填满型01背包

 

题目和分析都在下面啦

 

caioj1057【题意】
从前有个山洞里,洞有n个宝石,第i个宝石拿到市场能卖m[i]块钱,但是要拿走第i颗宝石需要花费t[i]秒的时间。
山洞V秒之后就会倒塌。
求在能活着离开山洞的前提下,获取的宝石总价值最大。
【输入文件】
第一行有两个整数V(1 <= V <= 1000)和n(1 <= n <= 100) 接下来的n行每行两个整数t[i] 和 m[i](范围0~100) 
【输出文件】
输出一行,一个整数,即最大总价值。


【样例输入】

【样例输出】

70 3

71 100

69 1

1 2

3

16  5

5 4

8 9

7 5

3 4

6 9

18

 


 

【数据规模】
对于30%的数据,n<= 10;

对于全部的数据,n <= 100。

 

 

【分析】

二维数组,填表格方法。

以第二组数据为例。假设我们要的答案就是f[5,16],就是眼前有5颗宝石且你有16秒的时间,你能取得的最大的价值量。

假如第5颗宝石你不想取,那么f[5,16]=f[4,16]

假如第5颗宝石你想拿,那么 f[5,16]=f[4,10]+9

 所以 f[5,16]=max( f[4,16] , f[4,10]+9)

16 5
5 4
8 9
7 5
3 4
6 9

具体实现如下表格:

f[i,j]

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

1

0

0

0

0

0

4

4

4

4

4

4

4

4

4

4

4

4

2

0

0

0

0

0

4

4

4

9

9

9

9

9

13

13

13

13

3

0

0

0

0

0

4

4

5

9

9

9

9

9

13

13

14

14

4

0

0

0

4

4

4

4

5

9

9

9

13

13

13

13

14

17

5

0

0

0

4

4

4

9

9

9

13

13

13

13

14

18

18

18

 


#include<bits/stdc++.h>
using namespace std;
int bg[150][10050],va[150],we[150];
int main()
{
 int m,n;   
 scanf("%d %d",&m,&n);
 for(int i=1;i<=n;i++)  cin>>we[i]>>va[i];
 for(int i=1;i<=n;i++){
    for(int j=m;j>=0;j--){
        if(we[i]<=j){
            bg[i][j]=max(bg[i-1][j],bg[i-1][j-we[i]]+va[i]);   //核心·状态转移方程
         }
         else bg[i][j]=bg[i-1][j];
     }
 }
   cout<<bg[n][m];
   return 0; 
}

 

//为了和之后的背包题联系起来,这里再贴另一份(优化)代码
#include<bits/stdc++.h> using namespace std; const int N=110000,M=11000; int f[N],t[M],v[M]; int main() { int n,T; scanf("%d%d",&T,&n); for(int i=1;i<=n;i++)scanf("%d%d",&t[i],&v[i]); for(int i=1;i<=n;i++){ for(int j=T;j>=1;j--){ if(j>=t[i]){ f[j]=max(f[j],f[j-t[i]]+v[i]); } } } printf("%d",f[T]); return 0; }

 

 

 

4.含价值的填满型完全背包

 

【题意】
小明背着一个背包(最大能带的重量为V)走进一个山洞,
山洞里有n 种 宝石(每种宝石无限多个),第 i 种 宝石的重量为v[i],拿到宝石店能卖m[i]块钱。
求在背包能承受重量的范围内,使得小明装进背包的宝石总价值最大。
【输入文件】
第一行有两个整数V(1 <= V <= 1000)和n(1 <= n <= 100)
接下来的n行每行两个整数v[i] 和 m[i](范围0~100)
【输出文件】
输出一行,一个整数,即最大总价值。

【样例输入】
70 3
71 100
69 1
1 2
【样例输出】
140
【数据规模】
对于30%的数据,n <= 10;
对于全部的数据,n<= 100。

#include<bits/stdc++.h>
using namespace std;
const int  N=110000,M=11000;
int f[N],t[M],v[M];
int main()
{
    int n,V;
    scanf("%d%d",&V,&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&t[i],&v[i]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=V;j++){  //只有这一句话变了(也就是只有查找的顺序变了/从前往后刷/尽量找占重量小的物品)
            if(j>=t[i]){
                f[j]=max(f[j],f[j-t[i]]+v[i]);
            }
        }
    }
    printf("%d",f[V]);
    return 0;
}

 

区间DP

刷题一时爽,一直刷题一直爽……

刷完这道题我就回来总结_(:3/_)__

 

 

子序列

 

  •  最长不下降子序列

         【例题1】

 

 

1
#include<iostream>
#include<cstdio>
using namespace std;

int a[3500],f[3500];
int main()
{
 int n,ans = 0;
 scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",a+i);

for(int i = 1; i <= n; i++)
 for(int j = 0; j < i; j++) 
{
 if(a[j] <= a[i])  f[i] = max(f[i],f[j] + 1); 

ans=max(ans,f[i]);
}
printf("%d\n",ans);
return 0; 
}

 

【例题2】IPUOJ10804

 

     

 

 1 2
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 const int N =3500;
 5 int ans=INT_MIN;
 6 int n,a[N],f[N][2];
 7 int main()
 8 {
 9  scanf("%d",&n);
10  for(int i =1; i <= n; i++)scanf("%d",&a[i]);
11  
12  for(int i =1; i <= n; i++){
13     for(int j =1; j < i; j++){
14       if(a[i]>= a[j]){
15        f[i][0]= max( f[i][0],f[j][0]+1);
16        f[i][1]= max( f[i][1],f[j][1]+1);
17     }
18      f[i][1]= max(f[i][1], f[j][0]+1);
19   }
20   ans = max(ans,max(f[i][1],f[i][0]));
21  }
22   printf("%d",ans +1);
23 }

 

 

 

 

 

 

 

 

 

 

【例题3】

 

 

 

#include<bits/stdc++.h>
using namespace std;
const int N=3500;
int n,K,ans,a[N],f[N][15];
int main()
{
 scanf("%d%d",&n,&K);
 for(int i = 1;i <= n;i++)scanf("%d", &a[i]);
 
 for(int i = 1; i <= n; i++){
  for(int j = 0; j < i; j++){//j是可以从0开始的,但一定要 < i 
   for(int k = 1; k <= K + 1; k++){
   if(a[j] <= a[i])
    f[i][k] = max(f[i][k], f[j][k] + 1);
    f[i][k] = max(f[i][k],f[j][k - 1] + 1);
    ans = max(ans,f[i][k]); 
     }
   }
  }
  printf("%d",ans);
}

 

 

 

 

转载于:https://www.cnblogs.com/phemiku/p/11375202.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值