20180516模拟赛T3——bag

题解

Cqz大佬在代码上的注释:

前i个物品,做成体积为j的东西,有多少种方案数

后i个物品,做成体积为j的东西,有多少种方案书(大佬打错了)

两个DP数组合并。 做不到?

其实就是把中间那段切断,然后把左右两边合并。

贴一段代码吧:

Rep(i,1,n)
{
	Dep(j,m,v[i])
		f[i][j] = (f[i-1][j] + f[i-1][j - v[i]])%Mod;
	Dep(j,v[i]-1,0)
		f[i][j] = f[i-1][j];
}
g[n+1][0] = 1;
Dep(i,n,1)
{
	Dep(j,m,v[i])
		g[i][j] = (g[i+1][j] + g[i+1][j - v[i]])%Mod;
	Dep(j,v[i]-1,0)
		g[i][j] = g[i+1][j];
}
Rep(i,1,n)
{
	ll ans = 0;
	Rep(j,0,m)
		ans = (ans + (1ll * f[i-1][j] * g[i+1][m-j] % Mod)) % Mod;//注意合并阶段一定要开long long
	writeln(ans);
}

zd大佬&sxd大佬的做法

先跑一遍01背包,要删除一段时,倒着跑一遍。请教了两位大佬,然而好像都无法证明。但在感性上应该是可以理解的(极其具有对称性)。

这个的代码就简单多了:

dp[0] = 1;
for(int i = 1; i <= n; ++i)
	for(int j = m; j >= v[i]; --j)
		dp[j] = (dp[j]+dp[j-v[i]])%mod;
for(int i = 1; i <= n; ++i)
{
	for(int j = 0; j <= m; ++j)
		f[j] = dp[j];
	for(int j = v[i]; j <= m; ++j)
		f[j] = ((f[j]-f[j-v[i]])%mod+mod)%mod;
	writeln(f[m]);
}

听说yyh大佬有一种容斥做法,然而我看不懂啊……只好直接贴代码了:

f[0]=1;
for(LL i=1; i<=n; i++)
{
	a[i]=read();
	for(LL j=m; j>=a[i]; j--)
		(f[j]+=f[j-a[i]])%=md;
}
for(LL i=1; i<=n; i++)
{
	ans=f[m];
	for(LL j=1; j*a[i]<=m; j++)
	{
		if(j&1)
			(ans-=f[m-j*a[i]])%=md;
		else
			(ans+=f[m-j*a[i]])%=md;
	}
	printf("%lld
",(ans%md+md)%md);
}
原文地址:https://www.cnblogs.com/pfypfy/p/9048088.html