題解 P3188 【[HNOI2007]夢幻島寶珠】 DP

~By Bartholomew~

其實,這道題目,評價:
1.難度: @@@@@
2.思維難度: @@@
3.細節難度: @@@@@
對於這道題目,我們發現其實就是普通的 01 揹包,但是數據 十分的大!
那麼我們可以對於 v = a ×2b ;的 b 相同的物品做一次 dp
那麼就是* f[i][j] * 表示體積爲 (j ×2i ) 的體積 只收納 k ×2i 的物品的最大值
那麼最後就是 怎麼合併是關鍵部分!
如果我們假設 f[i][j] 現在表示
表示20 ~~ 2i 組內, 容量爲 j ×2i +(w &((1 << i )1 )) 時的最大價值
也就是f[i,j]表示體積爲j × 2i 再加上W**二進制第i位以下的體積**最多可以獲得多少價值
那麼 我們的 答案其實就是 dp[len][1] , len指的是 最大的i2i 小於等於 m 的值!
那麼 我們就是考慮轉移 從 f[i1][...] 開始轉移!
轉移方程:
f[i,j] =max (f[i,j],f[i,jk] +f[i1] ,min(k2+e[i1],d[i1])] );
其中好多是小細節部分:

for(int j=w[i];j>=0;--j) 
for(int k=0;k<=j;k++)   // 這下面的f[i][j-k] (用"*"標註的)1其實並沒有變動,因爲是反着來求的,所以還是表示 單單是 2^i 的物品,大家可以寫一下式子,就明白了!     
      f[i][j] = getmax(f[i][j] , **f[i][j-k]** + f[i-1][min(w[i-1],(k<<1)|(m>>(i-1)&1))]);

貼上代碼 , 有明顯的小錯誤的哦!

#pragma GCC optimize(3)
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define N 200005
#define INF 0x3f3f3f3f
using namespace std;
int n,m,len,x,y,f[35][1150],w[35];
inline int read()
{
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9'){c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x;
}
vector<int> G[35],V[35];
inline int getmax(int x,int y){return (x>y)?x:y; }
inline void init()
{
    len = 0;
    memset(G,0,sizeof G);
    memset(V,0,sizeof V);
    memset(w,0,sizeof w);
    memset(f,0,sizeof f);
}
int main(int argc,char const* argv[])
{
    while(~scanf("%d%d",&n,&m) && n!=-1)
    {
        init();
        for(int i=1;i<=n;++i)
        {
            x = read(); int j = 0;
            while(!(x&1)) { x>>=1; j++; }
            G[j].push_back(x); w[j] += x;
            len = getmax( len , j);
            scanf("%d",&y); V[j].push_back(y);
        }
        for(int i=0;i<=len;i++)
            for(int j=0;j<(int)G[i].size();++j)
                for(int k=w[i];k>=G[i][j];k--)
                    f[i][k] = getmax(f[i][k],f[i][k-G[i][j]]+V[i][j]);
        while(m>>len) len++; len--;
        for(int i=1;i<=len;i++)         
        {
            w[i] += (w[i-1]+1)/2;
            for(int j=w[i];j>=0;--j) 
                for(int k=0;k<=j;k++)         
                    f[i][j] = getmax(f[i][j] , f[i][j-k] + f[i-1][min(w[i-1],(k<<1)|(m>>(i-1)&1))]);
        }
        printf("%d\n",f[len][1]); 
    }
    return main();
}
發佈了57 篇原創文章 · 獲贊 76 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章