dp-背包模型

一:01背包问题模型

 1 题目:

有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数)。

要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

输入格式

第一行是一个整数 V,表示箱子容量。

第二行是一个整数 n,表示物品数。

接下来 n 行,每行一个正整数(不超过10000),分别表示这 n 个物品的各自体积。

输出格式

一个整数,表示箱子剩余空间。

数据范围

<span id="MathJax-Span-2" class="mrow"><span id="MathJax-Span-3" class="mn">0<span id="MathJax-Span-4" class="mo">&lt;<span id="MathJax-Span-5" class="mi">V<span id="MathJax-Span-6" class="mo">&le;<span id="MathJax-Span-7" class="mn">200000<V≤20000,
<span id="MathJax-Span-9" class="mrow"><span id="MathJax-Span-10" class="mn">0<span id="MathJax-Span-11" class="mo">&lt;<span id="MathJax-Span-12" class="mi">n<span id="MathJax-Span-13" class="mo">&le;<span id="MathJax-Span-14" class="mn">30
0<n≤30

输入样例:

24
6
8
3
12
7
9
7

输出样例:

0

代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 20010;
 5 int dp[N];
 6 
 7 int main(){
 8     int V;cin >> V;
 9     int n;cin >> n;
10     for(int i = 1;i <= n;++i){
11         int v;cin >> v;
12         for(int j = V;j >= v;--j)
13             dp[j] = max(dp[j], dp[j - v] + v);
14     }
15     cout << V - dp[V];
16     return 0;
17 }
View Code

 

2 数字组合问题

题目:

给定N个正整数<span id="MathJax-Span-2" class="mrow"><span id="MathJax-Span-3" class="msubsup"><span id="MathJax-Span-4" class="mi">A<span id="MathJax-Span-5" class="mn">1<span id="MathJax-Span-6" class="mo">,<span id="MathJax-Span-7" class="msubsup"><span id="MathJax-Span-8" class="mi">A<span id="MathJax-Span-9" class="mn">2<span id="MathJax-Span-10" class="mo">,<span id="MathJax-Span-11" class="mo">&hellip;<span id="MathJax-Span-12" class="mo">,<span id="MathJax-Span-13" class="msubsup"><span id="MathJax-Span-14" class="mi">A<span id="MathJax-Span-15" class="mi">NA1,A2,…,AN,从中选出若干个数,使它们的和为M,求有多少种选择方案。

输入格式

第一行包含两个整数N和M。

第二行包含N个整数,表示<span id="MathJax-Span-17" class="mrow"><span id="MathJax-Span-18" class="msubsup"><span id="MathJax-Span-19" class="mi">A<span id="MathJax-Span-20" class="mn">1<span id="MathJax-Span-21" class="mo">,<span id="MathJax-Span-22" class="msubsup"><span id="MathJax-Span-23" class="mi">A<span id="MathJax-Span-24" class="mn">2<span id="MathJax-Span-25" class="mo">,<span id="MathJax-Span-26" class="mo">&hellip;<span id="MathJax-Span-27" class="mo">,<span id="MathJax-Span-28" class="msubsup"><span id="MathJax-Span-29" class="mi">A<span id="MathJax-Span-30" class="mi">NA1,A2,…,AN。

输出格式

包含一个整数,表示可选方案数。

数据范围

<span id="MathJax-Span-32" class="mrow"><span id="MathJax-Span-33" class="mn">1<span id="MathJax-Span-34" class="mo">&le;<span id="MathJax-Span-35" class="mi">N<span id="MathJax-Span-36" class="mo">&le;<span id="MathJax-Span-37" class="mn">1001≤N≤100,
<span id="MathJax-Span-39" class="mrow"><span id="MathJax-Span-40" class="mn">1<span id="MathJax-Span-41" class="mo">&le;<span id="MathJax-Span-42" class="mi">M<span id="MathJax-Span-43" class="mo">&le;<span id="MathJax-Span-44" class="mn">10000
1≤M≤10000,
<span id="MathJax-Span-46" class="mrow"><span id="MathJax-Span-47" class="mn">1<span id="MathJax-Span-48" class="mo">&le;<span id="MathJax-Span-49" class="msubsup"><span id="MathJax-Span-50" class="mi">A<span id="MathJax-Span-51" class="mi">i<span id="MathJax-Span-52" class="mo">&le;<span id="MathJax-Span-53" class="mn">1000
1≤Ai≤1000

输入样例:

4 4
1 1 2 2

输出样例:

3

代码:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 const int N = 10010;
 5 
 6 int dp[N];
 7 int arr[110];
 8 
 9 int main(){
10     int n, m;cin >> n >> m;
11     for(int i = 1;i <= n;++i)cin >> arr[i];
12     dp[0] = 1;
13     for(int i = 1;i <= n;++i)
14         for(int j = m;j >= arr[i];--j)
15             dp[j] = dp[j] + dp[j-arr[i]];
16     cout << dp[m];
17 }
View Code

 

数字组合问题2:

 

 

 

 二:完全背包模型:

1:题目:

小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。

问小明有多少种买书方案?(每种书可购买多本)

输入格式

一个整数 n,代表总共钱数。

输出格式

一个整数,代表选择方案种数。

数据范围

<span id="MathJax-Span-2" class="mrow"><span id="MathJax-Span-3" class="mn">0<span id="MathJax-Span-4" class="mo">&le;<span id="MathJax-Span-5" class="mi">n<span id="MathJax-Span-6" class="mo">&le;<span id="MathJax-Span-7" class="mn">10000≤n≤1000

输入样例1:

20

输出样例1:

2

输入样例2:

15

输出样例2:

0

输入样例3:

0

输出样例3:

1

 

算法分析:

 

 

代码:

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 const int N = 1010;
 6 
 7 int n;
 8 int v[4] = {10, 20, 50, 100};
 9 int f[N];
10 
11 int main()
12 {
13     cin >> n;
14 
15     f[0] = 1;
16     for (int i = 0; i < 4; i ++ )
17         for (int j = v[i]; j <= n; j ++ )
18             f[j] += f[j - v[i]];
19 
20     cout << f[n] << endl;
21 
22     return 0;
23 }
View Code

 

需要注意的是本题求的是方案数,也就是说手里的钱是必然全部花光的,而求价值的完全背包问题,手里的钱是不一定全部花光的

才用上面的代码定义的dp就是方案数,不会因为这个问题出错

 

 

2:稍微有点难度的完全背包问题

题目:

在网友的国度中共有 n 种不同面额的货币,第 i 种货币的面额为 a[i],你可以假设每一种货币都有无穷多张。

为了方便,我们把货币种数为 n、面额数组为 a[1..n] 的货币系统记作 (n,a)。 

在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i]× t[i] 的和为 x。

然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 x 不能被该货币系统表示出。

例如在货币系统 n=3, a=[2,5,9] 中,金额 1,3 就无法被表示出来。 

两个货币系统 (n,a) 和 (m,b) 是等价的,当且仅当对于任意非负整数 x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。 

现在网友们打算简化一下货币系统。

他们希望找到一个货币系统 (m,b),满足 (m,b) 与原来的货币系统 (n,a) 等价,且 m 尽可能的小。

他们希望你来协助完成这个艰巨的任务:找到最小的 m。

输入格式

输入文件的第一行包含一个整数 T,表示数据的组数。

接下来按照如下格式分别给出T组数据。 

每组数据的第一行包含一个正整数 n。

接下来一行包含 n 个由空格隔开的正整数 a[i]。

输出格式

输出文件共有T行,对于每组数据,输出一行一个正整数,表示所有与 (n,a) 等价的货币系统 (m,b) 中,最小的 m。

数据范围

<span id="MathJax-Span-2" class="mrow"><span id="MathJax-Span-3" class="mn">1<span id="MathJax-Span-4" class="mo">&le;<span id="MathJax-Span-5" class="mi">n<span id="MathJax-Span-6" class="mo">&le;<span id="MathJax-Span-7" class="mn">1001≤n≤100,
<span id="MathJax-Span-9" class="mrow"><span id="MathJax-Span-10" class="mn">1<span id="MathJax-Span-11" class="mo">&le;<span id="MathJax-Span-12" class="mi">a<span id="MathJax-Span-13" class="mo">[<span id="MathJax-Span-14" class="mi">i<span id="MathJax-Span-15" class="mo">]<span id="MathJax-Span-16" class="mo">&le;<span id="MathJax-Span-17" class="mn">25000
1≤a[i]≤25000,
<span id="MathJax-Span-19" class="mrow"><span id="MathJax-Span-20" class="mn">1<span id="MathJax-Span-21" class="mo">&le;<span id="MathJax-Span-22" class="mi">T<span id="MathJax-Span-23" class="mo">&le;<span id="MathJax-Span-24" class="mn">20
1≤T≤20

输入样例:

2 
4 
3 19 10 6 
5 
11 29 13 19 17 

输出样例:

2
5

 

思路分析
1:通过本题明白给定一个数组如何找出数组中除了他自己以外不可以被其他元素(其他元素可以选取的个数是无限的)通过相加的方式得到的数就可以,我们可以想到和完全背包问题相类似,所以我们可以借鉴一下完全背包的思路
2:dp[i] 表示 元素i可以被数组中的元素表示出来的的方案数,如果等于1那么可知改元素符合条件 ,计算求方案数才用和上面的题一样的方法
3:不可以被其他元素表示出来得到的数,一定可以通过某些相加的方式把其他元素表示出来

 

代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 25010;
 5 
 6 int dp[N];
 7 int arr[N];
 8 
 9 int main(){
10     int T;cin >> T;
11     while(T --){
12         int n;cin >> n;
13         for(int i = 1;i <= n;++i) cin >> arr[i];
14         
15         sort(arr, arr+1+n);
16         
17         int m = arr[n];
18         memset(dp, 0, sizeof dp);
19         dp[0] = 1;// 组成0的方案数是1
20         
21         for(int i = 1;i <= n;++i){
22             for(int j = arr[i];j <= m;++j)
23                 dp[j] = dp[j] + dp[j-arr[i]];
24         }
25         
26         int ans = 0;
27         for(int i = 1;i <= n;++i)
28             if(dp[arr[i]] == 1) ++ans;
29         cout << ans << endl;
30     }
31     return 0;
32 }
View Code

 

 
posted @ 2022-07-29 17:38  SXQ-BLOG  阅读(43)  评论(0编辑  收藏  举报