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