[bzoj4971][构造]记忆中的背包

版权声明:神的bolg... https://blog.csdn.net/Rose_max/article/details/86283570

Description

经过一天辛苦的工作,小Q进入了梦乡。他脑海中浮现出了刚进大学时学01背包的情景,那时还是大一萌新的小Q解
决了一道简单的01背包问题。这个问题是这样的:给定n个物品,每个物品的体积分别为v_1,v_2,…,v_n,请计算
从中选择一些物品(也可以不选),使得总体积恰好为w的方案数。因为答案可能非常大,你只需要输出答案对P取
模的结果。因为长期熬夜刷题,他只看到样例输入中的w和P,以及样例输出是k,看不清到底有几个物品,也看不
清每个物品的体积是多少。直到梦醒,小Q也没有看清n和v,请写一个程序,帮助小Q一起回忆曾经的样例输入。

Input

第一行包含一个正整数T(1<=T<=100),表示测试数据的组数。
接下来T行,每行3个整数w,P,k(50<=w<=20000,1<=P<=2^30,0<=k<=min(20000,P-1))
分别表示每组需要回忆的测试数据的相关参数。

Output

对于每组数据,第一行输出n(1<=n<=40)。
第二行输出n个正整数v_1,v_2,…,v_n(1<=v_i<=20000),分别表示每个物品的体积。
若有多组可行解,输出任意一组。输入数据保证对于每组数据至少存在一组可行解。

Sample Input

4

50 1013 4

50 3 1

80 5 1

扫描二维码关注公众号,回复: 5073290 查看本文章

100 1000000007 13

Sample Output

5

10 20 20 30 50

5

10 20 20 30 50

8

10 20 30 40 50 60 70 80

11

12 18 20 13 41 30 15 11 11 250 28

题解

非常吼的一道构造啊.
主要思路在于,我们要做到:两种物品分开来都不能贡献,只有合在一起的时候才能精准做出贡献
把物品分成两种:一种是大小为1的小物品,另一种是大小至少为 w 2 \lceil\frac{w}{2}\rceil 的大物品
显然只能将这两种物品组合在一起的时候才会有贡献
并且一定是一个大物品+一些小物品的组合
可以设一个 f [ i ] [ j ] f[i][j] 表示用了 i i 个小物品,方案数为 j j 的最少大物品数量
转移可以枚举这个大物品比 w w 小了k,显然他的贡献就是 C i k C_i^k
预处理一下就好了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=25;
const int MAXK=20005;
int C[MAXN][MAXN];
int f[MAXN][MAXK],pre[MAXN][MAXK];
void init()
{
	C[0][0]=1;
	for(int i=1;i<=20;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
}
int main()
{
	init();
	memset(f,63,sizeof(f));int inf=f[0][0];
	for(int i=0;i<=20;i++)
	{
		f[i][0]=0;
		for(int j=0;j<=20000;j++)
			for(int k=0;k<=i;k++)
				if(j+C[i][k]<=20000&&f[i][j+C[i][k]]>f[i][j]+1)
				{
					f[i][j+C[i][k]]=f[i][j]+1;
					pre[i][j+C[i][k]]=k;
				}
	}
	int T=read();while(T--)
	{
		int w=read(),P=read(),ans=read();
		if(!ans){puts("1\n1");continue;}
//		bool tf=false;
		for(int i=0;i<=20;i++)if(i+f[i][ans]<=40)
		{
			pr2(i+f[i][ans]);
			for(int j=1;j<=i;j++)pr1(1);
			int u1=ans;
			for(int j=1;j<=f[i][ans];j++)
			{
				pr1(w-pre[i][u1]);
				u1-=C[i][pre[i][u1]];
			}
			puts("");
			break;
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Rose_max/article/details/86283570