链接:http://218.28.19.228/cogs/problem/problem.php?pid=2230
题解:
论一类神兽的毒瘤数据和奇技淫巧题的解题技巧
朴素算法O(NM)妥妥要挂
但这道题考的是脑筋急转弯……
注意到v,w的数据范围特别小,也就是说所给出的物品中一定有许多是重复的
根据这个特点,可以类比计数排序的思路,用c[i][j]统计重量为i 价值为j的物品的个数,然后再把每个c[i][j]二进制拆分成单个物品,在进行01背包,就A了
只要能反应过来,这就是一道多重背包的裸题
然而出题人很心机orz
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define Nmax 40001
using namespace std;
int c[25][25];
int f[Nmax];
int w[Nmax],v[Nmax];
int cnt;
int main()
{
freopen("crazytime.in","r",stdin);
freopen("crazytime.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
int aa,bb,ma=0,mb=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&aa,&bb);
c[aa][bb]++;
ma=max(ma,aa);
mb=max(mb,bb);
}
for(int i=1;i<=ma;i++)
for(int j=1;j<=mb;j++)
{
int t=1;
while(c[i][j]>=t)
{
c[i][j]-=t;
w[++cnt]=t*i;
v[cnt]=t*j;
t<<=1;
}
if(c[i][j])
{
w[++cnt]=c[i][j]*i;
v[cnt]=c[i][j]*j;
c[i][j]=0;
}
}
for(int i=1;i<=cnt;i++)
for(int j=m;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
printf("%d",f[m]);
return 0;
}