【题目大意】6种价值分别为1 2 3 4 5 6的石头,给出每种石头的数目,问能不能将这些石头分为价值相等的两堆。
【解析】经典的多重背包问题,a[]数组即为1 2 3 4 5 6,count数组题目给出,怎么做呢?上面提到一点,设dp[i]表示容量为i的背包能不能被所给物体装满。如果dp[sum/2]==1,那么就说明可以分为两堆(sum是所有石头的总价值),直接套用上面的模板即可。
#include <cstdio>
#include <cstring>
using namespace std;
int a[10];
int dp[120100];
int main(){
int cas = 0;
while( ++ cas ){
int sum = 0;
for(int i = 1; i <= 6; ++i){
scanf("%d",&a[i]);
sum += a[i]*i;
}
if(sum == 0)break;
memset(dp,0,sizeof(dp));
printf("Collection #%d:\n",cas);
if(sum % 2 == 1){
puts("Can't be divided.");
puts("");
continue;
}
dp[0] = 1;
//O(NV)的方法
int dpt[120000];
for(int i = 1; i <= 6; ++i){
memset(dpt,0,sizeof(dpt));
for(int j = i; j <= sum/2; ++j){
if (!dp[j] && dp[j-i]&& dpt[j-i] < a[i]) {
dpt[j] = dpt[j-i] + 1;
dp[j] = 1;
}
}
}
/*
//二进制优化的方法
for(int i = 1;i <= 6;++i){
int k;
for(k = 1;k*2 < a[i]+1;k *= 2)
for(int v = sum/2;v >= k*i;--v)
if(dp[v-k*i])
dp[v]=true;
k = a[i]+1-k;
for(int v = sum/2;v > k*i;--v)
if([v-k*i])
dp[v] = true;
}
*/
if(dp[sum/2]){
puts("Can be divided.");
}
else {puts("Can't be divided.");};
puts("");
}
return 0;
}
【题目大意】给若干组物品,每组物品都有一个箱子(箱子自身也有cost),然后就是物品的cost和value,要买某个物品必须也要买装这个物品的箱子,给一定钱数,问能获得的最大价值。
【题目解析】可以参照背包九讲第六讲和第七讲里的内容解决该问题(相当对口,这题就是根据背包九讲来的)
有一种比较简单的写法,两次背包即可。
#include<iostream>
using namespace std;
const int MAXN = 100000;
int main()
{
int n,total,boxcost,goodnum,cost,value,i,j,t;
int dpbox[MAXN],dptotal[MAXN];
while(scanf("%d%d",&n,&total) != EOF)
{
memset(dptotal,0,sizeof(dptotal));
for(i = 0; i < n; i++)
{
scanf("%d%d",&boxcost,&goodnum);
memcpy(dpbox,dptotal,sizeof(dptotal));
for(j = 0; j < goodnum; j++)
{
scanf("%d%d",&cost,&value);
for(t = total - boxcost; t >= cost; t--)
{
if(dpbox[t] < dpbox[t - cost] + value)
dpbox[t] = dpbox[t-cost] + value;
}
}
for(t = total; t >= boxcost; t--)
if(dptotal[t] < dpbox[t-boxcost])
dptotal[t] = dpbox[t-boxcost];
}
printf("%d\n",dptotal[total]);
}
return 0;
}
【题目大意】一个人有T分钟去做工作,共有n组工作,每一组有若干工作,每个工作消耗的时间和得到的快乐值已知,每一个组有一个标识位,标识位为0,则该组中至少应该选取一样工作完成,标识位为1,则该组中至多有一个工作被选择,标识位为2,无限制。
【分析】对标识位为2的工作组,因为无限制条件,每个工作可做可不做,即用简单的01背包即可处理。对标识位为1的工作组,因为至多选择一个工作,所以本次操作只对dp数组选取最优值更新一次即可。对至少选择一个的工作组,还不是太理解,只是看别人代码写的,还请大家集思广益,有想法可以留言评论,以后理解透彻了以后会做补充。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
using namespace std;
const int maxt = 100 + 10;
int dp[maxt];
int td[maxt], n, m, t;
void dp0() { //至少取一个
memcpy (td, dp, sizeof(td));
memset (dp, -1, sizeof(dp));
for (int i = 0, a, b; i < m; ++i) {
scanf ("%d%d", &a, &b);
for (int j = t; j >= a; --j) {//注意怎样保证至少取一个??
if (dp[j-a] != -1) {
dp[j] = max(dp[j], dp[j-a] + b);
}
if (td[j-a] != -1) {
dp[j] = max(dp[j], td[j-a] + b);
}
}
}
}
void dp1() { //最多取一个,用上次得到的dp值更新最优值,只更新一次
memcpy(td, dp, sizeof(td));
for (int i = 0, a, b; i < m; ++i) {
scanf ("%d%d", &a, &b);
for (int j = t; j >= a; --j) {
if (td[j-a] != -1) {
dp[j] = max(dp[j], td[j-a] + b);
}
}
}
}
void dp2() { //无条件限制,01背包,更新多次
for (int i = 0, a, b; i < m; ++i) {
scanf ("%d%d", &a, &b);
for (int j = t; j >= a; --j) {
if (dp[j-a] != -1) {
dp[j] = max(dp[j], dp[j-a] + b);
}
}
}
}
void init() {
memset (dp, -1, sizeof(dp));
dp[0] = 0;
for (int i = 0, s; i < n; ++i) {
scanf ("%d%d", &m, &s);
if (s == 0) dp0();
else if (s == 1) dp1();
else if (s == 2) dp2();
}
}
void solve() {
int ans = -1;
for (int i = 0; i <= t; ++i)
ans = max(ans, dp[i]);
printf ("%d\n", ans);
}
int main() {
//freopen("ar.txt","r",stdin);
while (scanf ("%d%d", &n, &t) != EOF) {
init();
solve();
}
return 0;
}