POJ 1837 Balance(01背包)

POJ 1837 Balance(01背包)

http://poj.org/problem?id=1837

题意:

       有一个天平,天平左右两边各有若干个钩子,总共有C个钩子(每个钩子有相对于中心的距离,左负右正),有G个钩码,求将钩码全部挂到钩子上使天平平衡的方法的总数。

其中可以把天枰看做一个以x轴0点作为平衡点的横轴. 且每个砝码的重量不一定相同,且一个钩子下可以挂任意个不同的砝码.

分析:

       由于本题的砝码必须全部使用,所以不能算是彻底的01背包.

       我们把天平当前的平衡系数定为:  每个 砝码重量*它挂的钩子相对天平中心距离 的和.

       假设当前-3位置有2个重量20的砝码,1位置有一个10重量的砝码,那么当前天平的平衡系数==-3*2*20+1*10=-110.

       要使得天平平衡,就要让平衡系数==0.

       我们设状态dp[i-1][j]=x表示已经放置了前i-1个砝码(每个砝码可以放到任意钩子下)且当前平衡系数的值为j时, 共有x种方法.

       那么当我们放第i个砝码后会生成一个新的状态, 假设第i个砝码放在了相对距离C[k]的钩子下,那么有:

       dp[i][j+C[k]*W[i]] += dp[i-1][j].

       本来初值定为dp[0][0]=1,然后我们可以递推求出dp[G][0]的值即为所求. 但是C[k]*W[i]的值可能为负数(会使得数组下标为负数),且平衡系数值得范围为[-7500,到7500之间].15*20*25=7500.

       所以初值我们设为dp[0][7500]=1,然后递推求dp[G][7500]的值即可.dp[G][7500]的值可以这么理解: 原来我们dp[0][7500]==1表示不放任何砝码,且初始平衡系数就是7500时,有1种方法. 当我们放完G个砝码后,平衡系数还是7500(说明砝码的平衡系数抵消了)时,的方法数.

AC代码:

#include<cstdio>
#include<cstring>
using namespace std;

int n;//钩子数
int m;//砝码数
int C[20+5];//钩子相对距离
int G[20+5];//砝码重量
int dp[20+5][15000];
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&C[i]);
        for(int i=1;i<=m;i++)
            scanf("%d",&G[i]);

        memset(dp,0,sizeof(dp));
        dp[0][7500]=1;

        //用i的所有状态刷新i+1的所有状态
        for(int i=0;i<m;i++)
        for(int j=0;j<=15000;j++)if(dp[i][j])
        {
            for(int k=1;k<=n;k++)//选择G[i+1]砝码放置的位置
                dp[i+1][j+C[k]*G[i+1]] += dp[i][j];
        }

        printf("%d\n",dp[m][7500]);
    }
    return 0;
}

你可能感兴趣的:(Algorithm,算法,ACM)