開場先來一段百度百科:
動態規划中本階段的狀態往往是上一階段狀態和上一階段決策的結果。如果給定了第K階段的狀態Sk以及決策uk(Sk),則第K+1階段的狀態Sk+1也就完全確定。也就是說Sk+1與Sk,uk之間存在一種明確的數量對應關系,記為Tk(Sk,uk),即有Sk+1= Tk(Sk,uk)。 這種用函數表示前后階段關系的方程,稱為狀態轉移方程。在上例中狀態轉移方程為 Sk+1= uk(Sk) 。
poj1018題意:
目前有一個公司需要購進寬帶設備,每種設備有多款機器供選擇,每種設備都需購進一台,現給出每台設備的帶寬p與價格q,要求選擇設備的最小帶寬min(p)/add(q)(其中min(p)表示所有購進設備中最小的帶寬,add(q)表示所有購進設備的價格之和)為最大,並求出該值。
輸入:
第一行表示測試用例個數與設備種類數
接下來每一行第一個數表示每種設備有多少款機器,后面緊跟每款機器的帶寬與價格。
題目讀完,還是強調直覺,這題可使用動態規划得解;
分析動態規划關鍵點:
非常明顯這個問題可以進行分解,分解如下:
設dp[i][j]為前i組帶寬為j的最小價格和,那么狀態轉移方程為dp[i][j]=min{dp[i][j] , dp[i-1][k]+q},表示dp[i][j]為在兩種情況中取最小值:
1.j正好為最小帶寬,dp[i][j]就為前i組帶寬為j的價格和
2.之前的帶寬k為最小帶寬,價格為之前價格再加上q
代碼如下:
#include<iostream> #include<algorithm> using namespace std; const int inf = 0x3f3f3f3f; int a[120][1100]; //狀態轉移方程:dp[i][j] = min( dp[i][j] , dp[i-1][k]+q ) //dp[i][j]表示前i組帶寬為j的最小價格 int main(){ int n; cin>>n; while(n--){ int m; cin>>m; for(int i=1;i<=m;i++){ for(int j=0;j<1100;j++){ a[i][j]=inf; } } for(int i=1;i<=m;i++){ int num; cin>>num; for(int j=0;j<num;j++){ int p,q; cin>>p>>q; if(i==1){ a[i][p]=min(a[1][p],q); }else{ //之所以從0遍歷到1200: //1.預估帶寬最大到1200 //2.保證所有的帶寬與對應的最小價格都被存到二維數組中,最后所求最小價格即為a[3][min帶寬] for(int k=0;k<1100;k++){ if(a[i-1][k]!=inf){ if(k<=p){ a[i][k]=min(a[i][k],a[i-1][k]+q); }else{ a[i][p]=min(a[i][p],a[i-1][k]+q); } } } } } } double res=0; for(int i=0;i<1100;i++){ if(a[m][i]!=inf){ double temp = (double)i/a[m][i]; if(temp>res){ res=temp; } } } printf("%.3lf\n",res); } return 0; }