1. 程式人生 > >【隊內胡策 11.3】 T3

【隊內胡策 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; }