您的位置:首页 > 其它

ZOJ 3164 Cookie Choice(终极背包)

2012-11-08 17:18 369 查看
题意:

给出N类物品,有价值,数量,价格

还有一些限制组,限制的组里面最多只能选一类物品,问D钱最多能获得的价值,并且D钱必须花完

思路:

看完背包9讲,感觉这一题还是不难理解的。首先,对于没有组的限制的X类物品,进行一遍背包(01背包、完全背包、多重背包)。

其次,对于有组的限制的物品,就需要再次考虑下了。这次要用到泛化背包和分组背包的思想。每一组只能选择组里面的一类物品。

于是试图考虑,如果我分给这个组V(range from 0 to D)的容量,能够最多拿到多少价值。因为这个组有X类物品,只能选择一类,

所以需要先对这个组的每一类物品进行一次01背包,获得分配给这类物品V的容量取得的最大价值,然后再对这个组的X类物品进行分组背包。

最后,针对上述组进行一次分组背包,问题得以解决。

(第一次开始自己尝试描述算法思路,Orz Orz,坚持坚持)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

using namespace std;

#define max(a,b) (((a) > (b)) ? (a) : (b))

const int MAXN = 1500;
const int INF = 1e9;
int Ki[MAXN], Ei[MAXN], Pi[MAXN];
int w[10][MAXN], f[MAXN];
int n, d, g;

void init_input()
{
for (int i = 1; i <= n; ++i)
scanf("%d %d %d", &Ki[i], &Ei[i], &Pi[i]);

scanf("%d%*c", &g);
memset(f, 0, sizeof(f));
char s[1000];
for (int i = 1; i <= g; ++i)
{
gets(s);
int j = 0;
while (j < strlen(s))
{
if ('1' <= s[j] && s[j] <= '9')
{
int sum = 0;
while ('0' <= s[j] && s[j] <= '9')
sum = 10 * sum + s[j] - '0', ++j;
f[sum] = i;
}
else
++j;
}
}
}

void init_dp(int dp[])
{
fill(dp, dp + d + 1, -INF);
dp[0] = 0;
}

void complete_pack(int w, int v, int dp[])
{
for (int i = w; i <= d; ++i)
if (dp[i-w] > -INF)             // 必须装满的时候,是需要这么一个判断的。-INF表示没有合法解
dp[i] = max(dp[i], dp[i-w] + v);
}

void zero_one_pack(int w, int v, int dp[])
{
for (int i = d; i >= w; --i)
if (dp[i-w] > -INF)
dp[i] = max(dp[i], dp[i-w] + v);
}

void multiple_pack(int w, int v, int n, int dp[])
{
int i = 1, c = n;

while (i < c)
{
zero_one_pack(i * w, i * v, dp);
c -= i;
i *= 2;
}
if (c)
zero_one_pack(c * w, c * v, dp);
}

int main()
{
while (scanf("%d %d", &n, &d) != EOF)
{
init_input();

int dp[MAXN], temp[MAXN];

init_dp(dp);
for (int i = 1; i <= g; ++i)
init_dp(w[i]);

for (int i = 1; i <= n; ++i)
{
if (f[i])
init_dp(temp);

if (!Ki[i] || Ki[i] * Pi[i] >= d)
complete_pack(Pi[i], Ei[i], f[i] ? temp : dp);
else
multiple_pack(Pi[i], Ei[i], Ki[i], f[i] ? temp : dp);

if (f[i])
for (int j = 0; j <= d; ++j)
w[f[i]][j] = max(w[f[i]][j], temp[j]);
}
for (int i = 1; i <= g; ++i)    // w[g][d] : g个组,每个组有d个物品,每个组最多选择其中一个
for (int j = d; j >= 0; --j)
for (int k = 0; k <= j; ++k)
if (w[i][k] > -INF && dp[j-k] > -INF)
dp[j] = max(dp[j], dp[j-k] + w[i][k]);

if (dp[d] >= 0)
printf("%d\n", dp[d]);
else
printf("i'm sorry...\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: