最近刷题遇到好几道背包问题,背包问题是动态规则中的一类体型,在考察算法的笔试中经常遇到。
关于背包问题,文章 背包问题九讲 中已经做了很多分析,这里就不再细述,建议好好看看这篇文章。
然而文章给了许多案例分析,却没有很好的练习。
说明:
1、本文目的不在于讲解背包问题的分析与讲解,而是收集了一些背包问题。希望学习者学习背包问题的时候能找到一些对应的题加以练习。
2、本文根据我是刷题中遇到的背包问题,会不定期更新内容。
0-1 背包问题
1、兑换奖券
http://hihocoder.com/problemset/problem/1038?sid=842443
基本解法:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int i, j, n, m, need, val;
while (cin >> n >> m)
{
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (i = 1; i <= n; ++i)
{
cin >> need >> val;
for (j = 0; j <= m; ++j)
{
if(j<need)
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - need] + val);
}
}
cout << dp[n][m] << endl;
}
return 0;
}
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int i, j, n, m, need, val;
while (cin >> n >> m)
{
vector<int> dp(m + 1, 0);
for (i = 0; i < n; ++i)
{
cin >> need >> val;
for (j = m; j >= need; --j)
dp[j] = max(dp[j], dp[j - need] + val);
}
cout << dp[m] << endl;
}
return 0;
}
如果要输出最优方案,增加一个state状态数组,修改代码如下:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int i, j, n, m, need, val;
while (cin >> n >> m)
{
vector<int> dp(m + 1, 0), needs(n), vals(n);
vector<vector<int>> state(n, vector<int>(m + 1, 0));
for (i = 0; i < n; ++i)
{
cin >> need >> val;
needs[i] = need;
vals[i] = val;
for (j = m; j >= need; --j)
{
if (dp[j] < dp[j - need] + val)
{
dp[j] = dp[j - need] + val;
state[i][j] = 1;
}
}
}
cout << dp[m] << endl;
i = n;
j = m;
while (--i >= 0)
{
if (state[i][j] == 1)
{
cout << needs[i] << " ";
j -= vals[i];
}
}
}
return 0;
}
2、和为M的组合的个数(求方案数)
https://nanti.jisuanke.com/t/310
在N个数中找出其和为M的若干个数。先读入正整数N(1< N< 100)和M(1< M< 10000), 再读入N个正数(可以有相同的数字,每个数字均在1000以内), 在这N个数中找出若干个数, 使它们的和是M, 把满足条件的数字组合都找出来以统计组合的个数,输出组合的个数(不考虑组合是否相同)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n, m, coin;
cin >> n >> m;
vector<int> dp(m + 1, 0);
dp[0] = 1;
for (int i = 1; i <= n; ++i)
{
cin >> coin;
for (int j = m; j >=coin; --j)
dp[j] += dp[j - coin];
}
cout << dp[m] << endl;
return 0;
}
完全背包问题
1、兑换奖券
http://hihocoder.com/problemset/problem/1043?sid=842590
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int i, j, n, m, need, val;
while (cin >> n >> m)
{
vector<int> dp(m + 1, 0);
for (i = 0; i < n; ++i)
{
cin >> need >> val;
for (int j = need; j <= m; ++j)
dp[j] = max(dp[j], dp[j - need] + val);
}
cout << dp[m] << endl;
}
return 0;
}
2、换零钱(求方案数)
http://www.nowcoder.com/practice/185dc37412de446bbfff6bd21e4356ec
有一个数组changes,changes中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,对于一个给定值x,计算组成这个值的方案数。
int countWays(vector<int> changes, int n, int x)
{
int coin;
vector<int> dp(x + 1, 0);
dp[0] = 1;
for (int i = 0; i < n; ++i)
{
coin=changes[i];
for (int j = coin; j <=x; ++j)
dp[j] += dp[j - coin];
}
return dp[x];
}
3、换零钱(求最少硬币数)
https://leetcode.com/problems/coin-change/
分组背包问题
不超过N元钱:https://nanti.jisuanke.com/t/256
题解:http://blog.csdn.net/zmq570235977/article/details/52354503
泛化物品
提升英雄: http://hihocoder.com/problemset/problem/1091
题解:http://blog.csdn.net/zmq570235977/article/details/52151096