DP - hdu5291 Candy Distribution

来源:互联网 发布:编程电脑配置要求2017 编辑:程序博客网 时间:2024/05/17 03:05

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5291


题意:

给若干种糖果,每种糖果数量不一定相等,现需要将所有糖果分成两份,要求两份糖果数量相等,问有多少种分法?【可以不必将所有糖果分完,如全部不分,每份糖果数量为0,也是一种分法】


思路:

求有多少分法的题目一般都是dp……

dp[i][j]:只使用前i种糖果的情况下,第一份比第二份多j个糖果的分法总数,j的值可能为负数,处理时应加上一个常数使j恒为正数

朴素的dp转移方程:

dp[i][j+x] += dp[i-1][j]

其中x的取值是-arr[i] ~ arr[i],arr[i]为第i种糖果的数量,表示只用第i种糖果,第一份可以比第二份多x个

这样解时间复杂度是O(n^4),n=200,分别为n种糖果,n^2的糖果数量(j的取值应为所有糖果的总数*2),转移时每个状态可以转移到2*n个状态,所以共O(n^4)

显然前三个n是没法优化的,考虑能否把状态转移的时间复杂度降为O(1)

思考每个状态dp[i-1][j]对dp[i][...]的贡献,dp[i][j]=dp[i-1][j-x]*((arr[i]-x)/2+1) + dp[i-1][j-(x-1)]*((arr[i]-(x-1)/2+1)) + ... + dp[i-1][j]*(arr[i]/2+1) + ... + dp[i-1][j+x]*((arr[i]-x)/2+1)

可以看到dp[i][j]是dp[i-1][j-x] ~ dp[i-1][j+x]乘上对应权值之和,而权值序列是一个按奇偶序列分布的等差数列

若arr[i] = 3,权值序列为1 1 2 2 2 1 1

若arr[i] = 2,权值序列为1 1 2 1 1

观察找到规律,通过sum数组预处理,即可在O(1)的时间复杂度下完成状态转移 


代码:

<span style="font-family:Courier New;">#include<stdio.h>#include<iostream>#include<string>#include<string.h>#include<math.h>#include<algorithm>#include<vector>#include<queue>#include<stack>#include<map>#include<set>#include<functional>#pragma comment(linker, "/STACK:102400000,102400000")//C++using namespace std;const double PI = 3.141592653589793238462643383279502884197169399;const int MAXINT = 0x7fffffff;const int MAXSIZE = 200+30;const int MOD = 1e9 + 7;int main(){    int total;    int n;    long long dp[2*MAXSIZE*MAXSIZE];    int arr[MAXSIZE];    long long sum[2*MAXSIZE*MAXSIZE];    const int banl = MAXSIZE*MAXSIZE;    int candy=0;    cin>>total;    while (total--){        scanf("%d",&n);        memset(arr,0,sizeof(arr));        memset(dp,0,sizeof(dp));        candy = 0;        for (int i=0;i<n;++i){            scanf("%d",arr+i);            candy+=arr[i];        }        dp[banl] = 1;        for (int i=0;i<n;++i){            memset(sum,0,sizeof(sum));            if (arr[i]%2){                for (int j=-candy + banl - arr[i]; j <= -candy + banl;j+=2)                    sum[-candy + banl - 1] = sum[-candy + banl -1] + dp[j];                for (int j=-candy + banl - arr[i] + 1; j<= -candy + banl + 1; j+=2)                    sum[-candy + banl] = sum[-candy + banl] + dp[j];                for (int j=-candy + banl + 1;j<=candy + banl + arr[i]; ++j){                    sum[j] = sum[j-2] - dp[j-2-arr[i]+1] + dp[j];                }            }            else {                for (int j=-candy + banl - arr[i]; j <= -candy + banl;j+=2)                    sum[-candy + banl] = sum[-candy + banl] + dp[j];                for (int j=-candy + banl - arr[i] + 1; j<= -candy + banl + 1; j+=2)                    sum[-candy + banl + 1] = sum[-candy + banl + 1] + dp[j];                for (int j=-candy + banl + 2;j<=candy + banl + arr[i]; ++j){                    sum[j] = sum[j-2] - dp[j-arr[i]-2] + dp[j];                }            }            dp[-candy + banl] = (dp[-candy + banl] * (arr[i]/2+1)) % MOD;            for (int j=1;j<=arr[i];++j)                dp[-candy + banl] = (dp[-candy + banl] + dp[-candy + banl + j] * ((arr[i] - j)/2+1)) % MOD;            if (arr[i]%2){                for (int j=-candy + banl + 1;j<=candy + banl; ++j)                    dp[j] = (dp[j-1] - sum[j-2] + sum[j+arr[i]]) % MOD;            }            else{                for (int j=-candy + banl + 1;j<=candy+ banl; ++j)                    dp[j] = (dp[j-1] - sum[j-1] + sum[j+arr[i]]) % MOD;            }            //for (int k=-candy;k<=candy;++k){            //    cout<<"dp["<<i<<"]["<<k<<"]: "<<dp[banl+k]<<endl;            //}        }        //for (int k=-candy;k<=candy;++k){        //    cout<<"dp["<<n<<"]["<<k<<"]: "<<dp[banl+k]<<endl;        //}        int res = dp[banl];        res %= MOD;        res = (res+MOD)%MOD;        cout<<res<<endl;    }    return 0;}</span>



0 0
原创粉丝点击