POJ 1837 (DP)
题目:http://poj.org/problem?id=1837
一个天平,两个臂,两边有若干挂钩,给若干重物,把重物挂在挂钩上,使天平平衡,所有重物要用完,问一共有多少种方法?
首先,自定义平衡度(不知有没有):∑重量*力矩
显然平衡度为0时是平衡的。
定义balance[i][j]为用完前面 i 个重物,达到平衡度 j 的方法数,为出现负数啊!怎么办?因为最大的挂钩位置15,最大重物重量25,最大的重物数量20,所以最大的平衡度是7500(都在最右边),同理最小的平衡度是-7500,为避免下标为负数,就+7500,于是可定义全局数组 balance[21][15001]。初始平衡态为 balance[0][7500],0表示没放物体,7500是平衡的,因为逆着转回去就是0了撒!
既然是动态规划,就要找到子问题,定义状态,找到状态转移方程。
状态:balance[i][j]是用完前面 i 个重物,达到平衡度 j 的方法数
子问题:要知道balance[i][j] 只需知道没放第 i 个重物时的过渡状态(就是通过把第i个重物放在某个挂钩可以达到平衡度j),或者转化下:balance[i-1][j]把第i个重物放到某个位置达到的新的状态(便于代码实现)
状态转移方程: balance[i][ j+hook[k]*weight[i] ] += balance[i-1][j]; (i表示重物的下标,k表示挂钩的下标,j表示平衡度)
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> int balance[21][15001],hook[21],weight[21]; int main() { int h,w,i,j,k; while(scanf("%d%d",&h,&w) != EOF ) { for(i = 1 ; i <= h ; ++i) scanf("%d",hook+i); for(i = 1 ; i <= w ; ++i) scanf("%d",weight+i); /* 初始化 */ memset(balance,0,sizeof(balance)); balance[0][7500] = 1; /* 动态规划 */ for(i = 1 ; i <= w ; ++i)//遍历重物 { for(j = 15000 ; j >= 0 ; --j)//平衡度 { if(balance[i-1][j]) for(k = 1 ; k <= h;++k)//求下一状态 balance[i][ j+hook[k]*weight[i] ] += balance[i-1][j]; } } printf("%d\n",balance[w][7500]); } //system("pause"); return 0; }