我以为是个DP啊,而且考试的时候没明白这里的二维背包指的是什么(回去重修语文)
其实是个贪心,前缀和+枚举?
对于一个n * m的格子(也就是背包),如果只放进大小为3 * 1的物品,一般情况下可以放进n * m/3种。特殊情况:n或m为2,且另一个%3==2,格子会剩下4个,只能用1 * 2的来填满,所以最多放(n*m-4)/3个。如果知道了放几个1 * 3的格子,就可以O(1)算出1 * 2的物品可以放几个,也就是剩余格子数量除以2。
这样,可以枚举1 * 3的物品放几个,然后算出1 * 2的物品,同时贪心的思想,把两种物品按照价值由大到小排序,预处理前缀和,O(1)查询放任意多少物品的最大总价值。
特判: 2 * 2的格子
PS:
对于任意N * M的格子,最多放进的1 * 3物品的数量证明,我不是很清楚qwq。自己想了想大概是这样:
由于剩下的格子一定组成矩形(如果不是矩形,就不是按照最优摆法摆的),这个矩形如果有一个边长为3的情况,就还会放下更多的物品,所以边长最大为2,这样形成的矩形有三种,1 * 1,2 * 2,1 * 2,除了2 * 2的特殊情况,设剩余格子面积为s,s最大为2,其他两种填上的1 * 3的物品数一定是(N * M-s)/3,由于s<3,故这个值相当于(N * M/3)下取整。而且剩下的格子的面积s除以二就是可以放上的1 * 2的物品的数量
大家讨论这个问题的时候,关于5 * 5的格子,放下8个的方式有点特殊,是这样的:
所以这个结论还是对的
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10000+10;
int t,n,m,n2,n3,maxx,last,ans;
int f2[maxn],f3[maxn];
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
scanf("%d",&t);
while(t--)
{
ans=0;
scanf("%d%d%d%d",&n,&m,&n2,&n3);
for(int i=1;i<=n2;++i) scanf("%d",&f2[i]);
for(int i=1;i<=n3;++i) scanf("%d",&f3[i]);
sort(f2+1,f2+n2+1,cmp);
sort(f3+1,f3+n3+1,cmp);
for(int i=1;i<=n2;++i) f2[i]+=f2[i-1];
for(int i=1;i<=n3;++i) f3[i]+=f3[i-1];
if((m%3==2)&&(n%3==2)&&(m==2||n==2)) maxx=(m*n-4)/3;
else maxx=(m*n)/3;
maxx=min(maxx,n3);
if(m==2&&n==2) ans=f2[2];
else
for(int i=0;i<=maxx;++i)//别忘了不放3的情况
{
last=n*m-3*i;
ans=max(ans,f3[i]+f2[min(last>>1,n2)]);
}
printf("%d\n",ans);
}
return 0;
}