BZOJ 1190: [HNOI2007]夢幻島寶珠 揹包DP

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1049 Solved: 605

Description

給你N顆寶石,每顆寶石都有重量和價值。要你從這些寶石中選取一些寶石,保證總重量不超過W,且總價值最大爲,並輸出最大的總價值。數據範圍:N<=100;W<=2^30,並且保證每顆寶石的重量符合a*2^b(a<=10;b<=30)

Input

輸入文件中包含多組數據。每組數據的格式如下:第一行是兩個正整數n和W,1≤n≤100,1≤W≤2^30,分別表示寶石的數目和最多能帶走的寶石重量。接下來的n行,每行有兩個正整數weighti和valuei,1≤weighti≤2^30, 0≤valuei≤2^30,分別表示第i顆寶石的重量和價值,且保證weighti能寫成a*2^b(1≤a≤10,0≤b≤30)的形式。同一行的兩個正整數之間用空格隔開。最後一組數據的後面有兩個-1,表示文件的結束。這兩個-1並不代表一組數據,你不需對這組數據輸出結果。並且輸入文件中數據的組數不超過20。

Output

對於輸入的每組數據,輸出一個整數C,表示小P最多能帶走的寶石的總價值。每個結果整數C單獨佔一行,且保證C不會超過2^30。

Sample Input

4 10

8 9

5 8

4 6

2 5

4 13

8 9

5 8

4 6

2 5

16 75594681

393216 5533

2 77

32768 467

29360128 407840

112 68

24576 372

768 60

33554432 466099

16384 318

33554432 466090

2048 111

24576 350

9216 216

12582912 174768

16384 295

1024 76

-1 -1
Sample Output

14

19

1050650

HINT

Source


dalao講解

這就是神奇的分層揹包???

我們的狀態設計爲:
…………數位爲 9876543210
舉個例子 M = 1101010101

則dp[i][j]表示容量爲j2i 加上i 位以前的所有位的大小,比如說i5 ,則從左往右數4 位的權值直接選入

dp[5][3]表示M爲110101時的情況

這樣定義之後我們先按照一層一層地DP,把每一層,也就是i相同的情況DP出來,存在dp[i][j]中,注意這個時候dp[i][j]只表示了j2i 的情況

因此我們在層與層之間處理的時候
top表示m的最大位是第幾位
第一層for i 1 top
第二層for j 1000 0
第三層for k 0 j
dp[i][j] = max(dp[i][j], dp[i][ j - k ] + dp[ i - 1 ][ 2 * k + ( m >> ( i - 1 ) ) & 1 ] )
因爲我的j是逆序for j的,因此每次用到的情況都是沒有用到後面狀態的,也即每次都是隻是j2i 的情況來更新的


#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
const int MAXM = 35;
int n, m, cnt[MAXN];
long long dp[MAXM][MAXN];
int main( ) {
    while( scanf( "%d%d", &n, &m ) && n != -1 && m != -1 ) {
        long long ans = 0;
        memset( dp, 0, sizeof(dp) );
        for( register int i = 1; i <= n; i++ ) { int w, v, c = 0;
            scanf( "%d%d", &w, &v );
            while( w % 2 == 0 ) w /= 2, c++;
            for( register int j = 1000; j >= w; j-- ) 
                dp[c][j] = max( dp[c][ j - w ] + v, dp[c][j] );
            cnt[c] += w;
        }
        int top = 0;
        for( register int i = m; i; i >>= 1 ) top++; top--;
        for( register int i = 1; i <= top; i++ ) {
            for( register int j = 1000; j >= 0; j-- ) 
                for( register int k = 0; k <= j; k++ ) 
                    dp[i][j] = max( dp[i][j], dp[i][ j - k ] + dp[ i - 1 ][ min( 1000, ( k * 2 ) + ( m >> ( i - 1 ) & 1 ) ) ] );
        }
        printf( "%lld\n", dp[top][1] );
    }
    return 0;
}

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章