HDU 5000 Clone (2014年鞍山赛区网络赛D题)

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));
    }
}


你可能感兴趣的:(计数,01背包,ACM网络赛,等价转换)