介绍01背包与完全背包的思想与一些应用
一.01背包
/*
10 5
3 5
2 3
4 4
6 10
5 8
*/
/*
//方法一:
#include
#include
#include
using namespace std;
int main()
{
int m, n;
int a[100][100], w[100], v[100];
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
cin >> w[i] >> v[i];
}
for(int i = 0; i <= n; ++i)
{
a[0][i] = 0;
}
for(int i = 0; i <= m; ++i)
{
a[i][0] = 0;
}
for(int i = 1; i <= m; ++i)
{
for(int j = 0; j <= n; ++j)
{
if(j < w[i])
{
a[i][j] = a[i-1][j];
}
else
{
a[i][j] = max(a[i-1][j], a[i-1][j-w[i]]+v[i]);
}
}
}
cout << a[m][n] << endl;
return 0;
}
*/
/*
2 5
2 2
5 1
*/
#include
#include
#include
using namespace std;
int main()
{
int n, m;
int f[100], w[100], v[100];
cin >> m >> n;
for(int i = 1; i <= m; ++i)
{
cin >> w[i] >> v[i];
}
//为了实现背包装满,先把所有值赋值为一个极小的值,然后f[0] = 0;
// memset(f, -999999, sizeof(f));
// f[0] = 0;
//如果不用装满
memset(f, 0, sizeof(f));
for(int i = 1; i <= m; ++i)
{
for(int j = n; j >= w[i]; --j)
{
f[j] = max(f[j], f[j-w[i]]+v[i]);
}
}
cout << f[n] << endl;
return 0;
}
二.完全背包
/*
初始化的细节问题:
有的问题中有要求“恰好装满背包”,有的则没有要求,这就在初始化DP数组时有所不同。
恰好装满背包:则初始化时,DP[0]=0, 其他的DP[1…..V]均设为负的无穷大。
无须恰好装满背包:则初始化时,DP[1…..V]全部设为0;
*/
/*
描述
直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。本题要求是背包恰好装满背包时,求出最大价值总和是多少。如果不能恰好装满背包,输出NO
输入
第一行: N 表示有多少组测试数据(N<7)。
接下来每组测试数据的第一行有两个整数M,V。 M表示物品种类的数目,V表示背包的总容量。(0
#include
#include
using namespace std;
int main()
{
int n, m;
int c[100], w[100];
int f[100];
int num;
cin >> num;
while(num--)
{
cin >> m >> n;
for(int i = 1; i <= m; ++i)
{
cin >> c[i] >> w[i];
}
//为了实现背包装满,先把所有值赋值为一个极小的值,然后f[0] = 0;
memset(f, -999999, sizeof(f));
f[0] = 0;
//如果不用装满
/*
memset(f, 0, sizeof(f));
*/
for(int i = 1; i <= m; ++i)
{
for(int j = c[i]; j <= n; ++j)
{
f[j] = max(f[j], f[j-c[i]]+w[i]);
}
}
if(f[n] > 0)
{
cout << f[n] << endl;
}
else
{
cout << "NO!" << endl;
}
}
return 0;
}
/*
标题:包子凑数
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入
----
第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
输出
----
一个整数代表答案。如果凑不出的数目有无限多个,输出INF。
例如,
输入:
2
4
5
程序应该输出:
6
再例如,
输入:
2
4
6
程序应该输出:
INF
样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。
*/
#include
#include
using namespace std;
int f(int m, int n)
{
if(n == 0) return m;
else
{
return f(n, m%n);
}
}
int main()
{
int n;
int arr[101], b[10001] = {0};
cin >> n;
for(int i = 1; i <= n; ++i)
{
cin >> arr[i];
}
int flag = arr[1];
for(int i = 2; i <= n; ++i)
{
flag = f(flag, arr[i]);
}
if(flag != 1)
{
cout << "INF";
}
else
{
b[0] = true;
for(int i = 1; i <= n; ++i)
{
for(int j = arr[i]; j <= 100*100; ++j)
{
if(b[j-arr[i]])
{
b[j] = true;
}
}
}
int count = 0;
for(int i = 1; i <= 10000; ++i)
{
if(!b[i])
{
++count;
}
}
cout << count;
}
return 0;
}
题目:
L3-001. 凑零钱
时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越
韩梅梅喜欢满宇宙到处逛街。现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债。韩梅梅手边有104枚来自各个星球的硬币,需要请你帮她盘算一下,是否可能精确凑出要付的款额。
输入格式:
输入第一行给出两个正整数:N(<=104)是硬币的总个数,M(<=102)是韩梅梅要付的款额。第二行给出N枚硬币的正整数面值。数字间以空格分隔。
输出格式:
在一行中输出硬币的面值 V1 <= V2 <= ... <= Vk,满足条件 V1 + V2 + ... + Vk = M。数字间以1个空格分隔,行首尾不得有多余空格。若解不唯一,则输出最小序列。若无解,则输出“No Solution”。
注:我们说序列{A[1], A[2], ...}比{B[1], B[2], ...}“小”,是指存在 k >= 1 使得 A[i]=B[i] 对所有 i < k 成立,并且 A[k] < B[k]。
输入样例1:
8 9
5 9 8 7 2 3 4 1
输出样例1:
1 3 5
输入样例2:
4 8
7 2 4 3
输出样例2:
No Solution
以下列出三种可解方法
#include
#include
#include
#include
#include
#include
#include
using namespace std;
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int n, m;
bool f[10001];
bool flag[10001][101] = {0};
cin >> n >> m;
int w[10001];
for(int i = 1; i <= n; ++i)
{
cin >> w[i];
}
sort(w+1, w+n+1, cmp);
f[0] = 1;
for(int i = 1; i <= n; ++i)
{
for(int j = m; j >= w[i]; --j)
{
if(f[j-w[i]])
{
flag[i][j] = 1;
f[j] = 1;
}
}
}
vector v;
if(f[m] == 0)
{
cout << "No Solution" << endl;
return 0;
}
else
{
int a = n, b = m;
while(b)
{
if(flag[a][b])
{
v.push_back(w[a]);
b -= w[a];
}
--a;
}
}
for(int i = 0; i < v.size()-1; ++i)
{
cout << v[i] << " ";
}
cout << v[v.size()-1];
return 0;
}
#include
#include
#include
#include
#include
#include
#include
using namespace std;
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int n, m;
int f[10001];
bool flag[10001][101] = {0};
cin >> n >> m;
int w[10001];
for(int i = 1; i <= n; ++i)
{
cin >> w[i];
}
sort(w+1, w+n+1, cmp);
f[0] = 0;
for(int i = 1; i <= n; ++i)
{
for(int j = m; j >= w[i]; --j)
{
if(f[j] <= f[j-w[i]]+w[i])
{
flag[i][j] = 1;
f[j] = f[j-w[i]]+w[i];
}
}
}
vector v;
if(f[m] != m)
{
cout << "No Solution" << endl;
return 0;
}
else
{
int a = n, b = m;
while(b)
{
if(flag[a][b])
{
v.push_back(w[a]);
b -= w[a];
}
--a;
}
}
for(int i = 0; i < v.size()-1; ++i)
{
cout << v[i] << " ";
}
cout << v[v.size()-1];
return 0;
}
第三种不能的全部分数,原因好像是数组f的大小问题,提示段错误,此坑以后再补,注意用memset初始化数组的条件
#include
#include
#include
#include
#include
#include
#include
using namespace std;
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int n, m;
vector f(10010, -99999);
bool flag[10001][101] = {0};
cin >> n >> m;
int w[10001];
for(int i = 1; i <= n; ++i)
{
cin >> w[i];
}
sort(w+1, w+n+1, cmp);
f[0] = 0;
for(int i = 1; i <= n; ++i)
{
for(int j = m; j >= w[i]; --j)
{
if(f[j] <= f[j-w[i]]+w[i])
{
flag[i][j] = 1;
f[j] = f[j-w[i]]+w[i];
}
}
}
vector v;
if(f[m] == 0)
{
cout << "No Solution" << endl;
return 0;
}
else
{
int a = n, b = m;
while(b)
{
if(flag[a][b])
{
v.push_back(w[a]);
b -= w[a];
}
--a;
}
}
for(int i = 0; i < v.size()-1; ++i)
{
cout << v[i] << " ";
}
cout << v[v.size()-1];
return 0;
}