1. 程式人生 > >POJ 1837 Balance (DP-01揹包)

POJ 1837 Balance (DP-01揹包)

Balance
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 15217 Accepted: 9562

Description

Gigel has a strange "balance" and he wants to poise it. Actually, the device is different from any other ordinary balance. 
It orders two arms of negligible weight and each arm's length is 15. Some hooks are attached to these arms and Gigel wants to hang up some weights from his collection of G weights (1 <= G <= 20) knowing that these weights have distinct values in the range 1..25. Gigel may droop any weight of any hook but he is forced to use all the weights. 
Finally, Gigel managed to balance the device using the experience he gained at the National Olympiad in Informatics. Now he would like to know in how many ways the device can be balanced. 

Knowing the repartition of the hooks and the set of the weights write a program that calculates the number of possibilities to balance the device. 
It is guaranteed that will exist at least one solution for each test case at the evaluation. 

Input

The input has the following structure: 
• the first line contains the number C (2 <= C <= 20) and the number G (2 <= G <= 20); 
• the next line contains C integer numbers (these numbers are also distinct and sorted in ascending order) in the range -15..15 representing the repartition of the hooks; each number represents the position relative to the center of the balance on the X axis (when no weights are attached the device is balanced and lined up to the X axis; the absolute value of the distances represents the distance between the hook and the balance center and the sign of the numbers determines the arm of the balance to which the hook is attached: '-' for the left arm and '+' for the right arm); 
• on the next line there are G natural, distinct and sorted in ascending order numbers in the range 1..25 representing the weights' values. 

Output

The output contains the number M representing the number of possibilities to poise the balance.

Sample Input

2 4	
-2 3 
3 4 5 8

Sample Output

2

Source


提示:動態規劃,01揹包

初看此題第一個衝動就是窮舉。。。。不過再細想肯定行不通= =O(20^20)等著超時吧。。。

我也是看了前輩的意見才聯想到01揹包,用動態規劃來解

題目大意:

有一個天平,天平左右兩邊各有若干個鉤子,總共有C個鉤子,有G個鉤碼,求將鉤碼全部掛到鉤子上使天平平衡的方法的總數。

其中可以把天枰看做一個以x軸0點作為平衡點的橫軸

輸入:

2 4 //C 鉤子數 與 G鉤碼數

-2 3 //負數:左邊的鉤子距離天平中央的距離;正數:右邊的鉤子距離天平中央的距離c[k]

3 4 5 8 //G個重物的質量w[i]

dp思路:

每向天平中方一個重物,天平的狀態就會改變,而這個狀態可以由若干前一狀態獲得。

首先定義一個平衡度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]])

其實兩條方程是等價的,這個可以簡單驗證出來,而且若首先推導到第二條方程,也必須轉化為第一條方程,這是為了避免下標出現負數

結論:

最終轉化為01揹包問題

狀態方程dp[i][ j+ w[i]*c[k] ]= ∑(dp[i-1][j])

初始化:dp[0][7500] = 1;   //不掛任何重物時天枰平衡,此為一個方法

複雜度O(C*G*15000)  完全可以接受

  1. //Memory Time 
  2. //1496K  0MS 
  3. //我所使用的解題方法,由於dp狀態方程組申請空間比較大大
  4. //若dp為區域性陣列,則會部分機器執行程式時可能由於記憶體不足會無法響應
  5. //所以推薦定義dp為全域性陣列,優先分配記憶體
  6. #include<iostream>
  7. usingnamespace std;  
  8. int dp[21][15001]; //狀態陣列dp[i][j]
  9.                        //放入(掛上)前i個物品(鉤碼)後,達到j狀態的方法數
  10. int main(int i,int j,int k)  
  11. {  
  12.     int n;  //掛鉤數
  13.     int g;  //鉤碼數
  14.     int c[21];  //掛鉤位置
  15.     int w[21];  //鉤碼重量
  16.     /*Input*/
  17.     cin>>n>>g;  
  18.     for(i=1;i<=n;i++)  
  19.         cin>>c[i];  
  20.     for(i=1;i<=g;i++)  
  21.         cin>>w[i];  
  22.     /*Initial*/
  23.     memset(dp,0,sizeof(dp));  //達到每個狀態的方法數初始化為0
  24.     dp[0][7500]=1;     //7500為天枰達到平衡狀態時的平衡度
  25.                        //放入前0個物品後,天枰達到平衡狀態7500的方法有1個,就是不掛鉤碼
  26.     /*DP*/
  27.     for(i=1;i<=g;i++)  
  28.         for(j=0;j<=15000;j++)  
  29.             if(dp[i-1][j])  //優化,當放入i-1個物品時狀態j已經出現且被統計過方法數,則直接使用統計結果
  30.                             //否則忽略當前狀態j
  31.                 for(k=1;k<=n;k++)  
  32.                     dp[i][ j+w[i]*c[k] ] += dp[i-1][j]; //狀態方程
  33.     /*Output*/
  34.     cout<<dp[g][7500]<<endl;  
  35.     return 0;  
  36. }