【队内胡策 11.3】 T3

我以为是个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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值