1.题目描述:点击打开链接
2.解题思路:本题利用动态规划解决。首先需要一点点的推导,即什么情况下两个克隆体可以共存,假设A,B两个克隆体可以共存,必然有一个属性A大于B,而另一个A小于B,直观上感觉:如果他们的属性值的和sum相同的话,就一定可以共存。这一点很容易证明。
第二点,两个sum值不相同的克隆体能共存吗?显然也是可以的。然而当所有的sum值相同的克隆体都组成一类的话,如果有一个新的克隆体C,他的属性和为sumC,且sumC≠sum,假设一共有n个属性,那么必然可以从sum这一类克隆体中找到一个克隆体,有n-1个属性与C的属性一一对应,只有一个不相等。此时根据题意,这两个只可能是存在一个的。因此,只需要把所有sum值相等的克隆体全部找到即可。
然而sum值为多少的时候,这样的克隆体数量才是最多的呢?通过简单的举例分析可以发现,当和值sum为所有T[i]的和的一半时,克隆体存在的数量是最多的。至此,我们可以把问题转换为:有n个整数,每个数只能在0~T[i]的范围内变化,问这n个整数之和恰好为sum/2(此时的sum表示所有的T[i]的和)的方案数有多少?这就转化为经典的01背包问题了。下面的代码利用了“滚动数组”,由于状态转移方程为dp(i,s)=dp(i,s)+dp(i-1,s-j)(1≤j≤T[i]),如果我们逆序枚举和值s,并且将i的维度去掉,由于s-j<s,即先更新s,然后才能更新s-j,而更新dp(s)时候用到的dp(s-j)即为dp(i-1,s-j)。因此,降维后的表达式可以写为dp(s)=dp(s)+dp(s-j)。详细过程见代码。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;
const int N=2000+10;
const int MOD=1e9+7;
int a[N];
int dp[N];
int n;
int solve(int sum)
{
me(dp);
dp[0]=1;
for(int i=0;i<n;i++)
for(int s=sum;s>0;s--)
for(int j=1;j<=a[i]&&j<=s;j++)
dp[s]=(dp[s]+dp[s-j])%MOD;//利用滚动数组计算,由于dp(s-j)晚于dp(s)更新,因此它还是前一轮计算的结果
return dp[sum];
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int sum=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
printf("%d\n",solve(sum/2));
}
}