1. 程式人生 > >Bzoj P3717 [PA2014]Pakowanie___狀壓dp

Bzoj P3717 [PA2014]Pakowanie___狀壓dp

題目大意:

你有nn個物品,體積分別為a1,a2,...,an1,ana_1,a_2,...,a_{n-1},a_n 你也有mm個包,容量分別為c1,c2,...,cm1,cmc_1,c_2,...,c_{m-1},c_m 物品不可被分割,一個包可以放多個物品,問把所有物品裝入包中,至少需要幾個包?

1<=n<=24,1<=m<=1001<=n<=24,1<=m<=100

1<=m<=100 1&lt;=a[i]&lt;=1081&lt;=a[i]&lt;=10^8 1&lt;=c[i]&lt;=1081&lt;=c[i]&lt;=10^8

分析:

顯然對於nn個物品,最壞的情況就是一個物品放進一個包,那麼我們只需要保留最大的nn個包 然後我們發現因為n24n≤24,所以可以想到用狀壓dp 設f[i]f[i]表示選了的物品狀態為ii時需要用到的最少的包的數量 然後設g[i]g[i]表示在用到的最少的包的數量為f

[i]f[i]時,這些包中還能接受的最大體積,即最大揹包剩餘容積 轉移顯然

程式碼:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <algorithm>
 
#define inf 0x3f3f3f3f
#define N 24
 
using namespace std;
 
int f[(1<<N)+5], g[(1<<N)+5], B[N*5+5], A[N+5], n, m;
 
bool cmp(int aa, int bb)
{
    return aa > bb; 
}
 
int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &A[i]);
    for (int i = 1; i <= m; i++) scanf("%d", &B[i]);
    sort(B + 1, B + m + 1, cmp);
    int MaxNum = (1 << n) - 1;
    for (int i = 1; i <= MaxNum; i++) f[i] = inf;
    for (int i = 1; i <= MaxNum; i++)
        for (int j = 1; j <= n; j++)
            if ((i >> (j - 1)) & 1) 
            {
               int k = i & (~(1 << (j - 1)));
               if (g[k] >= A[j])
               {
                  if (f[i] > f[k] || (f[k] == f[i] && g[k] - A[j] > g[i])) 
                     f[i] = f[k], g[i] = g[k] - A[j];
               } 
               else if (B[f[k] + 1] >= A[j]) 
               {
                  if (f[i] > f[k] + 1 || (f[k] + 1 == f[i] && B[f[k] + 1] - A[j] > g[i])) 
                     f[i] = f[k] + 1, g[i] = B[f[k] + 1] - A[j];                   
               }
            }
    if (f[MaxNum] == inf) printf("NIE"); else printf("%d",f[MaxNum]); 
    return 0;
}