poj1837 动态规划和01背包问题延伸的经典题目,很值得一做

首先说一下基本思路,是按照小优博客上来的思路,即平衡度来做的,其博主在博客上写得已经非常全面了,这里转载一下:

来源:http://blog.csdn.net/lyy289065406/article/details/6648094/

每向天平中方一个重物,天平的状态就会改变,而这个状态可以由若干前一状态获得。
 
首先定义一个平衡度j的概念
当平衡度j=0时,说明天枰达到平衡,j>0,说明天枰倾向右边(x轴右半轴),j<0则相反
那么此时可以把平衡度j看做为衡量当前天枰状态的一个值
因此可以定义一个 状态数组dp[i][j],意为在挂满前i个钩码时,平衡度为j的挂法的数量。
由于距离c[i]的范围是-15~15,钩码重量的范围是1~25,钩码数量最大是20
因此最极端的平衡度是所有物体都挂在最远端,因此平衡度最大值为j=15*20*25=7500。原则上就应该有dp[ 1~20 ][-7500 ~ 7500 ]。
因此为了不让下标出现负数,做一个处理,使使得数组开为 dp[1~20][0~15000],则当j=7500时天枰为平衡状态
 
那么每次挂上一个钩码后,对平衡状态的影响因素就是每个钩码的 力臂
力臂=重量 *臂长 = w[i]*c[k]
那么若在挂上第i个砝码之前,天枰的平衡度为j
   (换言之把前i-1个钩码全部挂上天枰后,天枰的平衡度为j)
则挂上第i个钩码后,即把前i个钩码全部挂上天枰后,天枰的平衡度 j=j+ w[i]*c[k]
   其中c[k]为天枰上钩子的位置,代表第i个钩码挂在不同位置会产生不同的平衡度
 
不难想到,假设 dp[i-1][j] 的值已知,设dp[i-1][j]=num
               (即已知把前i-1个钩码全部挂上天枰后得到状态j的方法有num次)
   那么dp[i][ j+ w[i]*c[k] ] = dp[i-1][j] = num
(即以此为前提,在第k个钩子挂上第i个钩码后,得到状态j+ w[i]*c[k]的方法也为num次)
 
想到这里,利用递归思想,不难得出 状态方程dp[i][ j+ w[i]*c[k] ]= ∑(dp[i-1][j])
有些前辈推导方式稍微有点不同,得到的 状态方程为dp[i][j] =∑(dp[i - 1][j - c[i] * w[i]])
 
其实两条方程是等价的,这个可以简单验证出来,而且若首先推导到第二条方程,也必须转化为第一条方程,这是为了避免下标出现负数


下面回顾一下动态规划题目的基本步骤:

第一步,确定递归方程;

第二步,初始化dp数组;

第三步,确定那些不能通过递归方程做的一些例子,给它们赋值;

第四步,开始for循环解题。


本题中第三步的是dp[i][j]中所有i==0的情况。

代码如下:

#include
using namespace std;

int weightNum;
int hookNum;
int hook[21];
int weight[21];
int dp[21][15001];
 
int main(){
	cin>>hookNum>>weightNum;
	for(int i=1;i<=hookNum;i++){
		cin>>hook[i];
	}
	for(int i=1;i<=weightNum;i++){
		cin>>weight[i];
	}
	for(int i=1;i<=15000;i++){
		dp[0][i]=0;
	} 
	for(int i=1;i<=weightNum;i++){
		for(int j=1;j<=15000;j++){
			dp[i][j]=0;
			for(k=1;k<=hookNum;k++){
				dp[i][j]+=dp[i-1][j-hook[k]*weight[i]];
			}
		}
	}
	cout<


你可能感兴趣的:(各类ACM题目)