POJ-1837 Balance---01背包

题目链接:

https://cn.vjudge.net/problem/POJ-1837

题目大意:

一个天平上有C个挂钩,第i个挂钩的位置为C[i],C[i] < 0表示该挂钩在原点的左边,C[i] > 0表示该挂钩在原点的右边;然后给出G个钩码的重量,问有多少种挂法使得天平保持平衡。

解题思路:

分析:当天平平衡时,每向天平上挂一个钩码,天平的状态就会改变,而这个状态可以由若干前一状态获得。

首先定义一个平衡度j的概念

当平衡度j=0时,说明天枰达到平衡,j>0,说明天枰倾向右边(x轴右半轴),j<0则相反

那么此时可以把平衡度j看做衡量当前天枰状态的一个值

因此可以定义一个 状态数组dp[i][j],意为在挂满前i个钩码时,平衡度为j的挂法的数量。

由于距离L[i]的范围是-15~15,钩码重量的范围是w[i]是1~25,钩码数量最大是20

因此最极端的平衡度是所有物体都挂在最远端,因此平衡度最大值为j=15*20*25=7500。原则上就应该有dp[ 1~20 ][-7500 ~ 7500 ]。

因此为了不让下标出现负数,做一个处理,使得数组开为 dp[1~20][0~15000],令7500对应0,则当j=7500时天枰为平衡状态。

代码中设置10000为平衡状态

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #include<stack>
 8 #include<map>
 9 #include<sstream>
10 #define Mem(a, b) memset(a, b, sizeof(a))
11 using namespace std;
12 typedef long long ll;
13 int dp[21][20005];
14 //dp[i][j]表示前i个物品平衡度为j的状态的种数
15 //由于左端平衡度小于0,右端大于0,所以全部偏移10000即可(因为最大平衡度只有7500)
16 int l[21], w[21];
17 int main()
18 {
19     int n, m;
20     scanf("%d%d", &n, &m);
21     for(int i = 1; i <= n; i++)scanf("%d", &l[i]);
22     for(int i = 1; i <= m; i++)scanf("%d", &w[i]);
23     dp[0][10000] = 1;//初始化设置平衡度为10000时,两端平衡,前0个物品使得两端平衡的情况有1种,即两端都不放
24     for(int i = 1; i <= m; i++)//枚举第i件物品
25     {
26         for(int j = 1; j <= 20000; j++)//枚举每一个状态
27             if(dp[i - 1][j])//如果dp[i - 1][j]不为0,状态可以转移
28         {
29             for(int k = 1; k <= n; k++)//枚举钩子,也就是每个物品的不同平衡度的时候放置每个钩子上
30                 dp[i][j + l[k] * w[i]] += dp[i - 1][j];
31         }
32     }
33     cout<<dp[m][10000]<<endl;
34     return 0;
35 }
原文地址:https://www.cnblogs.com/fzl194/p/9011350.html