题目链接:题目传送门
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<ctype.h>
#include<queue>
#include<vector>
#include<set>
#include<cstdio>
#include<cmath>
#define mem(a,x) memset(a,x,sizeof(a))
#define inf 1<<30
#define NN 10004
using namespace std;
const double PI = acos(-1.0);
typedef long long LL;
/**********************************************************************
N个单词,总复杂度V
输入单词,单词的复杂度ci,价值wi
求在总复杂度V范围内能获得的最大价值
01背包--然而超时了。。。
于是开始优化:
题目给了很特别的范围:0<=ci,wi<=10
仔细想想会发现,最多只能组成121(11*11)件物品,但是题目给了100000件
于是想到很多物品是重复的,换多重背包写
**********************************************************************/
int note[13][13];//note[i][j]表示体积i价值j的物品的数量
int f[NN];
int c[123],w[123],num[123];
int N,V;
void ZeroOnePack(int ci,int wi)
{
for (int v = V; v >= ci; v--)
{
f[v] = max(f[v],f[v-ci]+wi);
}
}
void CompletePack(int ci,int wi)
{
for (int v = ci; v <= V; v++)
{
f[v] = max(f[v],f[v-ci]+wi);
}
}
int main()
{
while (cin>>N>>V)
{
mem(f,0);
mem(note,0);
char s[20];
for (int i = 0,c,w; i < N; i++)
{
scanf("%s%d%d",s,&w,&c);
note[c][w]++;
}
int n = 0;
for (int i = 0; i <= 10; i++)
{
for (int j = 0; j <= 10; j++)
{
if (note[i][j] > 0)
{
n++;
c[n] = i,w[n] = j,num[n] = note[i][j];
}
}
}
//下面就是多重背包,多重背包开二维也是超时,于是上一维转化01、完全的做法
for (int i = 1; i <= n; i++)
{
if (c[i]*num[i] >= V)
CompletePack(c[i],w[i]);
else
{
int k = 1,rest = num[i];
while (k <= rest)
{
ZeroOnePack(k*c[i],k*w[i]);
rest -= k;
k *= 2;//二进制拆分的方法
}
ZeroOnePack(rest*c[i],rest*w[i]);//剩下数量为rest
}
}
printf("%d\n",f[V]);
}
return 0;
}