演算法 動態規劃poj1837

2021-07-13 16:49:26 字數 3260 閱讀 9708

複習了動態規劃の0-1揹包問題,核心方程就是

if(c[i] > j)   f[i][j] = f[i-1][j];  )//如果揹包的容量,放不下c[i],則不選c[i]            

else

f[i][j] = max(f[i-1][j], f[i-1][j - c[i]] + v[i]);//這裡註明一點,i指代物品數量,j指代揹包容積,那麼有兩種情況,乙個是不要第i件物品,那麼就需要指導f[i-1][j],乙個是要第i件物品,這時候就需要知道f[i-1][j-c[i]]。

ps

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

(為什麼是求和呢?一開始沒想明白,後來明白了,這個可以這麼想先把i當作定值,即現在我們求得就是i個物品的各種平衡度,因為該方程是放在j和k的迴圈體下的,所以j+w[i]*c[k]其實可能有很多種j和k的組合啦,所以方法數應該累加的。

優you

題目大意:

有乙個天平,天平左右兩邊各有若干個鉤子,總共有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)  完全可以接受

//memory time

//1496k 0ms

//我所使用的解題方法,由於dp狀態方程組申請空間比較大大

//若dp為區域性陣列,則會部分機器執行程式時可能由於記憶體不足會無法響應

//所以推薦定義dp為全域性陣列,優先分配記憶體

#includeusing namespace std;

int dp[21][15001]; //狀態陣列dp[i][j]

//放入(掛上)前i個物品(鉤碼)後,達到j狀態的方法數

int main(int i,int j,int k)

{ int n; //掛鉤數

int g; //鉤碼數

int c[21]; //掛鉤位置

int w[21]; //鉤碼重量

/*input*/

cin>>n>>g;

for(i=1;i<=n;i++)

cin>>c[i];

for(i=1;i<=g;i++)

cin>>w[i];

/*initial*/

memset(dp,0,sizeof(dp)); //達到每個狀態的方法數初始化為0

dp[0][7500]=1; //7500為天枰達到平衡狀態時的平衡度

//放入前0個物品後,天枰達到平衡狀態7500的方法有1個,就是不掛鉤碼

/*dp*/

for(i=1;i<=g;i++)

for(j=0;j<=15000;j++)

if(dp[i-1][j]) //優化,當放入i-1個物品時狀態j已經出現且被統計過方法數,則直接使用統計結果

//否則忽略當前狀態j

for(k=1;k<=n;k++)

dp[i][ j+w[i]*c[k] ] += dp[i-1][j]; //狀態方程

/*output*/

cout<(為什麼是求和呢?一開始沒想明白,後來明白了,這個可以這麼想先把i當作定值,即現在我們求得就是i個物品的各種平衡度

POJ 1837 天平問題

主要參考 題目 題意 有乙個天平,左臂右臂各長15,然後給出n,m,n代表有幾個掛鉤,掛鉤給出負數代表在左臂的距離,正數則在右臂 m代表有m個砝碼,要你求出使得這個天平保持平衡有幾種方法,要求所有砝碼全部使用完 思路 首先我們先要明確dp陣列的作用,dp i j 中,i為放置的砝碼數量,j為平衡狀態...

動態規劃 POJ 1837 Balance

這段時間要沉迷刷題一段時間了,就讓csdn陪我一起吧!題目的大致意思是說,有乙個天平,題目給出天平上具有的鉤子數量為c,擁有的物品數量為g,現在要求你要利用這些重物,讓天平平衡,當然要求是要把全部的重物都掛到鉤子上,允許有鉤子空著。結果是要求輸出可以是天平達到平衡的懸掛方法種數。這種問題一看就是動態...

poj 1837 Balance 動態規劃

使用迭代器對stl容器進行遍歷的方法 for set iterator it check.begin it check.end it it 本題 a存掛鉤位置 b存物品質量 把掛在天平左邊的物品的質量視為負數 反之為正數 總質量的極限為20件重25的物品都掛在15的天平掛鉤處 即7500 dp i ...