BZOJ4971:記憶中的揹包

題目
玄學構造題,01揹包的逆向構造。首先可以明確模數是沒用的, 我們一定可以構造出方案數小於模數的一組數據(因爲只有揹包體積w已知的條件下,可以構造出方案數爲任意值的物品數據)。這玩意兒構造的自由度太高,還有就是數不止一個,且互相之間沒啥關係,不好下手。題解的方法是構造這樣一組數據:由x個1和若干個w/2(向上取整)的大數,對於這樣一組數據,我們必須且只能選一個大數a,對總方案數的貢獻是C(x,w-a)(從x中取w-a得方案數);
令dp[i][j] 表示i個1,方案數爲j所需要的最少的大數物品個數,那麼狀態轉移方程就是:
dp[i][j+C[i][k]] = min(dp[i][j+C[i][k]],dp[i][j] + 1);
最後我們就可以記錄dp計算過程,構造出這個序列;

對於枚舉的1的個數的範圍肯定不會超過40。但是具體還能縮小,不知道怎麼縮的

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<map>
#include<bitset>
#include<queue>
#define lson (rt << 1)
#define rson (rt << 1 | 1)
const int maxn = 100000+10;
const int maxm = 4e5 + 10;
const int inf_max = 0x3f3f3f;
const int mod = 1000000007;
using namespace std;
typedef long long ll;
int w,p,K;
int dp[45][20010],path[45][20010];
int C[45][45];
void pre_c() {
    C[0][0] = 1;
    for(int i = 1;i <= 25; ++i) {
        C[i][0] = 1;
        for(int j = 1;j <= 25; ++j) {
            C[i][j] = C[i-1][j-1] + C[i-1][j];
        }
    }
}

int main()
{
    pre_c();
    memset(dp,inf_max,sizeof(dp));
    for(int i = 0;i <= 25; ++i) dp[i][0] = 0;
    for(int i = 0;i <= 25; ++i) {
        for(int j = 0;j <= 20000; ++j) {
            for(int k = 0;k <= i; ++k) {
                if(j + C[i][k] <= 20000 && dp[i][j + C[i][k]] > dp[i][j] + 1) {
                    dp[i][j+C[i][k]] = dp[i][j] + 1;  //dp[i][j]可以添加一個體積是w-k的大數物品得到dp[i][j+C[i][k]]這個狀態
                    path[i][j+C[i][k]] = k; //記錄dp過程的信息
                }
            }
        }
    }
    int T;
    cin>>T;
    while(T--) {
        cin>>w>>p>>K;
        for(int i = 1;i <= 25; ++i) {
            if(dp[i][K] + i <= 40) {  //如果滿足條件就輸出
                printf("%d\n",dp[i][K] + i); 
                for(int j = 1;j <= i; ++j) printf("1 "); //1的個數
                while(K) { //K代表當前的方案數
                    int tt = path[i][K];
                    printf("%d ",w-tt); 
                    K -= C[i][tt];  //少了一個大數物品體積w-tt,減去這個物品的貢獻C[i][tt];
                }
                printf("\n");
                break;
            }
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章