背包分类:0-1背包、完全背包、分组背包、多重背包。
01背包(ZeroOnePack):有N件物品和一个容量为V的背包。每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
完全背包(CompletePack):有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
抢劫多个银行逃跑的概率等于抢劫每个银行逃跑概率之积。
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
double dp[10010];
struct node
{
int v;
double p;
}a[110];
int main()
{
int t, n, sum;
double p;
cin >> t;
while (t--)
{
cin >> p >> n;
sum = 0;
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++)
{
cin >> a[i].v >> a[i].p;
sum += a[i].v;
}
dp[0] = 1;
for (int i = 0; i < n; i++)
{
for (int j = sum; j >= a[i].v; j--)
{
dp[j] = max(dp[j], dp[j - a[i].v] * (1 - a[i].p));
}
}
for (int i = sum; i >= 0; i--)
{
if (dp[i] > (1 - p))
{
cout << i << endl;
break;
}
}
}
return 0;
}
输入数据有多组,对于每组数据第一行输入n,m,k,s(0 < n,m,k,s < 100)四个正整数。分别表示还需的经验值,保留的忍耐度,怪的种数和最多的杀怪数。接下来输入k行数据。每行数据输入两个正整数a,b(0 < a,b < 20);分别表示杀掉一只这种怪xhd会得到的经验值和会减掉的忍耐度。(每种怪都有无数个)
Output
输出升完这级还能保留的最大忍耐度,如果无法升完这级输出-1。
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int dp[105][105];
int main()
{
int n, m, k, s, a, b;
while (cin >> n >> m >> k >> s)
{
int num = inf;
memset(dp, 0, sizeof(dp));
for (int i = 0; i < k; i++) //k种怪
{
cin >> a >> b; //收益-代价
for (int j = b; j <= m; j++) //消耗(代价)容忍度 b->m
{
for (int d = 0; d <= s; d++) //打怪数目
{
dp[j][d] = max(dp[j][d], dp[j - b][d - 1] + a);
if (dp[j][d] >= n)
{
num = min(num, j);
}
}
}
}
if (num <= m)cout << m - num << endl;
else cout << "-1" << endl;
}
return 0;
}
多重背包
多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:
f[i][v]=max{ f[i-1][v-k*c[i]] + k*w[i] | 0<=k<=n[i]}
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int dp[100010], a[110], c[110];
int n, m;
//m背包的总容量、v物品的体积、w物品的价值
void OneZeroPack(int m, int v, int w) //0-1背包
{
for (int i = m; i >= v; i--)
dp[i] = max(dp[i], dp[i - v] + w);
}
//m背包的总容量、v物品的体积、w物品的价值
void CompletePack(int m, int v, int w) //完全背包
{
for (int i = v; i <= m; i++)
dp[i] = max(dp[i], dp[i - v] + w);
}
//m背包的总容量、v物品的体积、w物品的价值、num物品的数量
void MultiplePack(int m, int v, int w, int num)//多重背包
{
if (v*num >= m) //此时相当于物品数量无限进行完全背包
{
CompletePack(m, v, w);
return;
}
int k = 1; //否则进行01背包转化,具体由代码下数学定理可得
for (k = 1; k <= num; k <<= 1)
{
OneZeroPack(m, k*v, k*w);
num = num - k;
}
if (num)
OneZeroPack(m, num*v, num*w);
}
int main()
{
while (cin >> n >> m)
{
if (n == 0 && m == 0) break;
for (int i = 0; i<n; i++) cin >> a[i];
for (int i = 0; i<n; i++) cin >> c[i];
for (int i = 0; i <= m; i++) dp[i] = -INF;
dp[0] = 0;
for (int i = 0; i<n; i++)
{
MultiplePack(m, a[i], a[i], c[i]);
}
int sum = 0;
for (int i = 1; i <= m; i++)
if (dp[i]>0) sum++;
cout << sum << endl;
}
return 0;
}
定理:一个正整数n可以被分解成1,2,4,…,2^(k-1),n-2^k+1(k是满足n-2^k+1>0的最大整数)的形式,且1~n之内的所有整数均可以唯一表示成1,2,4,…,2^(k-1),n-2^k+1中某几个数的和的形式。
woj 1537 A Stone-I 转化成背包
woj 1538 B Stone-II 转化成背包
poj 1170 Shopping Offers 状压+背包
zoj 3769 Diablo III 带限制条件的背包
zoj 3638 Fruit Ninja 背包的转化成组合数学
hdu 3092 Least common multiple 转化成完全背包问题
poj 1015 Jury Compromise 扩大区间+输出路径
poj 1112 Team Them UP 图论+背包