POJ - 1837 Balance 天平(二維01揹包)


提示:動態規劃,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)  完全可以接受




#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
#include<vector>
using namespace std;
int c[33],g[33];
int dp[33][16000];
const int dis = 7500;
int main()
{
    int C,G,i,j,k;
    scanf("%d%d",&C,&G);
    for(i=1;i<=C;i++) scanf("%d",&c[i]);
    for(i=1;i<=G;i++) scanf("%d",&g[i]);
    dp[0][dis]=1;
    for(i=1;i<=G;i++) {
        for(j=7500;j>=-7500;j--) {
            for(k=1;k<=C;k++) {
                if(j-g[i]*c[k]>=-7500 && j-g[i]*c[k]<=7500) {
                    dp[i][j+dis]+=dp[i-1][j-g[i]*c[k]+dis];
                }
            }
        }
    }
    printf("%d\n",dp[G][dis]);
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章