題目地址在:http://poj.org/problem?id=1837
做這個題目的開始,真的還沒有什麼思路,即使有人說是01背包,不過依然不知道如何將問題裝換成01背包的模型。
在比較經典的背包問題中,一般會有一些物品,物品用(Wi, Vi)組成,Wi表示其重量,Vi表示其價值。 然後給一個容量固定的背包,然後選擇物品,達到價值最大這個目標。
1. 當物品可以分割的時候,貪心演算法
2. 當物品只有一件的時候,是01背包
3. 當物品有無數件的時候,是完全背包
4. 當物品有固定件的時候,是部分背包
這個題目的描述如下:
有一個天平,天平上有若干的掛鈎,給你若干的砝碼,求有多少種方法,可以讓天平平衡。
這個題目我讀了半天才搞明白什麼意思。 用圖一下子就明白了。
天平上的鉤子,就是紅點所示的東東,而砝碼就是綠色的東東。 這裡可能利用到一些物理知識, 天平平衡條件應該是
∑ wi * di = ∑ wj * dj。
其中i表示左側的組合,j表示右側,w表示各自重量,di表示到天平中心的距離。
力矩 = 力 * 力臂,這樣依據平衡狀態而得到的 DP 方程如下。
DP[i] [j + weights[i-1]*armLen[k]] += DP[i-1][j];
也等價於
DP[i] [j] += DP[i-1][j - weights[i-1]*armLen[k]];
現在我們描述所有力矩的狀態,dp[i][j] 表示當我們放完了第 i 個砝碼的時候,力矩是 j 的所有放法數。
如果直接用 j 會出現問題,因為左力矩是負的,不過我們可以找出一個比較大的數,將所有狀態 j 加上這個數,就得到一個非負數的狀態。
我們這裡 砝碼最大 25, 鉤子最遠是 15, 最多有20 個砝碼。可知 最大的狀態應該是 [-7500, 7500], 所有我們可以將每個狀態加上 7500, 將狀態變成 [0, 15000].
Source CodeProblem: 1837User: hopeztmMemory: 780KTime: 47MSLanguage: C++Result: AcceptedSource Code#include <stdio.h>#include <memory.h>#define MAX_STATUS 7500#define MAX_NUM 21int armLen[MAX_NUM];int weights[MAX_NUM];int nArm, nWeight;int DP[MAX_NUM][MAX_STATUS];int main(){int i,j; while(scanf("%d%d", &nArm, &nWeight) != EOF){memset(DP, 0, sizeof(DP)); //input for( i = 0; i < nArm; i++){scanf("%d", armLen + i); }int weightSum = 0;for( i = 0; i < nWeight; i++){scanf("%d", weights + i);weightSum += weights[i];} //get max and min status IDconst int maxStatus = armLen[nArm - 1] * weightSum;const int minStatus = armLen[0] * weightSum;DP[0][MAX_STATUS] = 1; // if we put 0 weight, and the balanced is balancedfor( i = 1; i <= nWeight; i++) // for all weights{for( j = minStatus; j <= maxStatus; j++){for( int k = 0; k < nArm ; k++){DP[i][j + weights[i-1]*armLen[k] + MAX_STATUS] += DP[i-1][j + MAX_STATUS]; }}}printf("%d\n", DP[nWeight][MAX_STATUS]);}return 0;}