[BZOJ]2287 消失之物 線段樹分治|揹包

2287: 【POJ Challenge】消失之物

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 875  Solved: 499
[Submit][Status][Discuss]

Description

ftiasch 有 N 個物品, 體積分別是 W1W2, ..., WN。 由於她的疏忽, 第 i 個物品丟失了。 “要使用剩下的 N - 1 物品裝滿容積爲 x 的揹包,有幾種方法呢?” -- 這是經典的問題了。她把答案記爲 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i, x) 表格。

Input

 

第1行:兩個整數 N (1 ≤ N ≤ 2 × 103) 和 M (1 ≤ M ≤ 2 × 103),物品的數量和最大的容積。

第2行: N 個整數 W1W2, ..., WN, 物品的體積。

Output

 

一個 N × M 的矩陣, Count(i, x)的末位數字。

Sample Input

3 2
1 1 2

Sample Output

11
11
21

HINT

如果物品3丟失的話,只有一種方法裝滿容量是2的揹包,即選擇物品1和物品2。

Source

[Submit][Status][Discuss]

HOME Back

  新年開的第一道題... 話說csdn又改版了?? 怎麼感覺就是像大白皮膚靠攏啊. markdown界面變小不過全屏模式很爽啊, xhtml編輯模式更新了好評.

  這道題可以用線段樹分治或者揹包做. 

  首先我們考慮線段樹分治.  暴力揹包怎麼做呢? for每個物品的時候把其他物品加進來, 這樣是n^2v的, 顯然過不了此題. 我們發現有些物品被重複加進去了很多次. 這是因爲第i個物品被加進去的區間實際上就是[1, i) 和(i, n]. 那麼我們就可以考慮線段樹分治, 把for的順序當做時間, 對時間進行線段樹分治. 把每個點所存在的時間映射成兩個區間. 在線段樹分治的過程中就像線段樹那樣, 如果某個物品完整包括當前區間就把它加進揹包, 否則分類討論下方或分裂再下放區間即可(就是線段樹裏面L<=mid, R>mid的操作), 那麼到葉子結點我們就把所有覆蓋當前點的物品加了進來, 此時的揹包也就是題目中關於i所要求的揹包辣. 由於每個揹包的存在區間最多2個(1, n只有1個), 那麼由線段樹的性質可以知道最多分成2 logn個區間, 那麼每個物品最多被加入揹包2logn次, 時間複雜度也就是O(nvlogn). 那麼具體實現可以參考po姐的實現. 本蒟蒻就嘴巴Ac了

  那麼用揹包怎麼做呢? 我們看到這種限制很簡單的就要想到補集轉化辣. count(i, x)就是填滿體積爲j的揹包的方案數 - 用了i號物品的填滿體積爲j的揹包的方案數. 那麼設c[i][j]表示不用i號物品填滿體積爲j的揹包的方案數, f[j]爲填滿體積爲j的揹包的方案數. 那麼用了i號物品的填滿體積爲j的揹包的方案數怎麼算呢? 對於揹包問題我們其實可以在空間上限制來算. c[i][j - w[i]]實際上就表達了這種方案數. 因爲我們只需要欽定最後一個物品爲i即可. 爲什麼不是f[j - w[i]]? 因爲有可能這種方案裏面也用了i. 那麼i就被用了兩次, 這是不合法的. 當w[i] < j的時候, c[i][j] = f[j], 因爲不可能用到i號物品. 那麼我們就可以遞推完成此題辣.

  實現過程中發現枚舉i的時候c數組只與當前i有關... 省掉一維XD.

#include<bits/stdc++.h>
using namespace std;
short n, m;
short a[2005], c[2005], f[2005];
int main() {
    scanf("%d%d", &n, &m);
    register short i, j;
    for (i = 1; i <= n; ++ i) scanf("%d", &a[i]);
    f[0] = 1;
    for (i = 1; i <= n; ++ i)
        for (j = m; j >= a[i]; -- j) {
            f[j] += f[j - a[i]];
            if (f[j] >= 10) f[j] -= 10;
        }
    for (i = 1; i <= n; ++ i, puts("")) {
        for (j = 1; j <= m; ++ j) c[j] = 0;
        c[0] = 1;
        for (j = 1; j <= m; ++ j) {
            if (j >= a[i]) c[j] = f[j] - c[j - a[i]];
            else c[j] = f[j];
            if (c[j] < 0) c[j] += 10; 
        }
        for (j = 1; j <= m; ++ j) printf("%d", c[j]);
    }
    return 0;
} 


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