2018/07/10测试T2 Knapsack

【题目】

题目描述:

有 n 个物品,第 i 个物品的重量为 ai 。
设 f(i,j,k,l,m) 为满足以下约束的物品集合数量:
1、集合中所有物品的重量和恰好为 m 。
2、集合包含物品 i 和物品 j 。
3、集合不包含物品 k 和物品 l 。

给出一个正整数 s ,求:


答案对 10^9+7 取模。

输入格式:

第一行,两个正整数 n,s 。
第二行,n 个正整数 ai,描述每个物品的重量。

输出格式:

输出一行,一个整数表示答案对 10^9+7 取模后的结果。

样例数据:

输入:

4 4

1 2 3 4

输出:

8

备注:

数据规模与约定:

对于 30% 的数据,n,s≤10。
对于另 20% 的数据,ai=1。
对于 80% 的数据,n,s≤100。
对于 100% 的数据,n,s≤1000。


【分析】

emmm…… 这道题,我连暴力都写挂了,心态爆炸

这个问题等价于,枚举所有总重量不超过s的集合,根据这个集合的元素个数,计算合法的(i,j)(k,l)对数

有一种较为简单的方法是定义数组f[i][j]来表示选了i个数后总数为j的方案个数,那就可以用背包问题的思路来做,这样做的话是O(n^3)的复杂度,但这种方法竟然神奇地过了90分

正解做法是记录i,j中确定了几个,k,l中确定了几个,状态数为3^2,同时计算背包,复杂度为O(3^2*n*s),另外,最终答案要乘4,因为在DP过程中相当于规定了i<j,k<l,而在实际情况中并不一定是这样


【代码】

先是简单的做法(90分):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1005;
const int mod=1e9+7;
int f[N][N];                                     //f[i][j]表示选了i个数总和为j的方案数 
int main()
{
//	freopen("knapsack.in","r",stdin);
//	freopen("knapsack.out","w",stdout);
	int n,s,i,j,k,x,ans=0;
	scanf("%d%d",&n,&s);
	f[0][0]=1;
	for(i=1;i<=n;++i)
	{
		scanf("%d",&x);
		for(j=n;j>=1;--j)
		  for(k=s-x;k>=0;--k)
		    if(f[j-1][k])
		      f[j][k+x]=(f[j][k+x]+f[j-1][k])%mod;
	}
	for(i=1;i<=n;++i)
	{
		for(j=1;j<=s;++j)
		{
			x=1;
			x=(1LL*x*i)%(1LL*mod);
			x=(1LL*x*(i-1))%(1LL*mod);
			x=(1LL*x*(n-i))%(1LL*mod);
			x=(1LL*x*(n-i-1))%(1LL*mod);
			x=(1LL*x*f[i][j])%(1LL*mod);
			ans=(ans+x)%mod;
		}
	}
	printf("%d",ans);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

正解做法:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2005;
const int mod=1e9+7;
int f[N][3][3],g[N][3][3];
void add(int &x,int y)
{
	x=x+y;
	if(x>=mod)  x-=mod;
}
int main()
{
//	freopen("knapsack.in","r",stdin);
//	freopen("knapsack.out","w",stdout);
	int n,s,i,j,k,l,x,ans=0;
	scanf("%d%d",&n,&s);
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	f[0][0][0]=1;
	g[0][0][0]=1;
	for(i=1;i<=n;++i)
	{
		scanf("%d",&x);
		for(j=0;j<=s;++j)
		  for(k=0;k<=2;++k)
		    for(l=0;l<=2;++l)
		    {
		    	if(j+x<=s)
		    	{
		    		add(g[j+x][k][l],f[j][k][l]);
		    		if(k<2)  add(g[j+x][k+1][l],f[j][k][l]);
		    	}
		    	if(l<2)  add(g[j][k][l+1],f[j][k][l]);
		    }
		memcpy(f,g,sizeof(f));
	}
	for(i=1;i<=s;++i)
	  ans=(ans+4ll*f[i][2][2])%mod; 
	printf("%d",ans);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/80988642