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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章