【BZOJ1190】夢幻島寶珠(HNOI2007)-揹包DP+思維
阿新 • • 發佈:2018-11-07
測試地址:夢幻島寶珠
做法: 本題需要用到揹包DP+思維。
這道題看上去是一個裸01揹包,然而容量特別大,因此我們只能從其中唯一一個特殊條件入手:
形式的重量。
我們考慮把這些物品分階段來進行決策。我們首先對每個
,求出重量表示為
的那些物品中,取重量為
的物品能得到的最大總價值
,這就是一個一般的01揹包了,因為
,所以時間複雜度最多是
的級別。接下來,我們從低位向高位DP,令
為取用
的物品,容量為
加上
在第
位以下的所有容量時所能得到的最大總價值。上面
在第
位以下的容量,就指的是它在第
位以下的上界(用位運算解釋就是
)。在轉移的時候,我們首先列舉當前的
,然後看前一位有哪些可以轉移到當前狀態的狀態。顯然,由於要滿足第
位之前是上界的條件,第
位應該和
的第
位相同,這才能轉移。於是我們就寫出來了一個狀態轉移的式子,就解決了這道題,時間複雜度雖然看上去有點大,但均攤下來還是跑得很快的。
以下是本人程式碼:
#include <bits/stdc++.h>
using namespace std;
int n,W,totw[35],f[35][2100],len;
vector<int> w[35],val[35];
int main()
{
while(scanf("%d%d",&n,&W)&&n>=0&&W>=0)
{
len=0;
for(int i=0;i<35;i++)
w[i].clear(),val[i].clear();
memset(totw,0,sizeof(totw));
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int j=0;
while(!(x&1)) {j++;x>>=1;}
w[j].push_back(x);
totw[j]+=x;
val[j].push_back(y);
len=max(len,j);
}
while(W>>len) len++;
len--;
for(int i=0;i<=len;i++)
for(int j=0;j<(int)w[i].size();j++)
for(int k=totw[i];k>=w[i][j];k--)
f[i][k]=max(f[i][k],f[i][k-w[i][j]]+val[i][j]);
for(int i=1;i<=len;i++)
{
totw[i]+=((totw[i-1]+1)>>1);
for(int j=totw[i];j>=0;j--)
for(int k=0;k<=j;k++)
f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(totw[i-1],(k<<1)|((W>>(i-1))&1))]);
}
printf("%d\n",f[len][1]);
}
return 0;
}