UVA 10465 Homer Simpson(完全背包: 二維目標條件),10465homer
UVA 10465 Homer Simpson(完全背包: 二維目標條件)
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1406
題意:
有兩種漢堡包(漢堡數量無限多),第一種吃一個需要花n分鐘,第二種吃一個需要花m分鐘. 現在你有t分鐘的時間, 問你最少浪費幾分鐘不能吃漢堡(你每次要麼完整的吃完一個漢堡,要麼不吃). 當吃漢堡花費的時間達到最大時, 問你最多能吃幾個漢堡?
分析:
本題的限制條件是: 總時間<=t分鐘.
本題的目標條件是: 總時間盡量大, 如果時間相同的情況下漢堡數目越多越好.
二維(甚至多維)目標條件有兩種方法可以做.
第一種方法是:先用完全背包求出最大時間tmax, 然後再用一次完全背包求出吃漢堡的時間正好等於最大時間tmax下, 最多能吃幾個漢堡.
首先用完全背包求出最大時間tmax. 令dp[i][j]==x表示當決策完前i個漢堡後, 總時間不超過j分鐘時最多能花x分鐘. 那麼有下面遞推公式:
dp[i][j] = max( dp[i-1][j] , dp[i][j-time[i]]+time[i] )
前者表示一個i 漢堡都不選,後者表示至少選1個i漢堡.
初始化為dp全0. 最終tmax=dp[n][t].
然後我們求在花費時間正好tmax的情況下的最大漢堡數. 令dp[i][j]==x 表示決策完全i個物品後, 時間正好為j分鐘時的最大漢堡數目為x個. 那麼有下面遞推公式:
dp[i][j] = max( dp[i-1][j] , dp[i][j-time[i]]+1)
前者表示一個i 漢堡都不選,後者表示至少選1個i漢堡.
初始化為dp全-1.且dp[0][0]=0.
最終最大漢堡數=dp[n][tmax].
第二種方法是:
UVA12563題目一樣的思想:
http://blog.csdn.net/u013480600/article/details/40376143
一般我們做的背包問題都是問你<=t的時間內, 如何選擇哪些漢堡(在不超過總時間的前提下)能使得吃的時間最長或 吃的漢堡最多. 但是本題需要同時考慮兩個最優條件, 那麼該怎麼做呢?
我們令dp[i][j]==x 表示當決策完全前i個物品後(選或不選), 吃漢堡花的時間<=j時, 所得到的最優狀態為x. (這裡的x就不是平時我們所說的最長時間或最多歌曲數目了)
怎麼理解最優狀態為x這個事實呢? 假設有兩種選擇前i個漢堡的方法能使得決策完前i個物品且總時間長度<=j時的狀態分別為x1 和x2.
那麼如果x1狀態的吃漢堡時間> x2狀態的吃漢堡時間, 那麼明顯x1狀態更優. 所以dp[i][j]應==x1.
如果x1狀態的吃漢堡時間與x2的相等, 但是x2狀態的吃漢堡數目 > x1狀態的吃漢堡數目, 那麼此時x2狀態更優. 所以dp[i][j]應==x2.
經過上面的分析,我們可以用一個(具有吃漢堡時間和吃漢堡數目雙屬性的)結構體來表示一個狀態. 且可以得到下面狀態轉移公式:
dp[i][j] = 最優(dp[i-1][j] , 在dp[i-1][j-time[i]]的基礎上選擇第i個漢堡後得到的新狀態tmp )
所有dp初始化為0即可. 最終我們所求為dp[n][t]
程式實現用的滾動數組,所以dp只有[j]這一維.
AC代碼1:用方法1兩次DP做的
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=10000+5;int n,m,t;//對應題意的含義int time[5];//吃第i種漢堡所花時間int dp[maxn];int main(){ while(scanf("%d%d%d",&time[1],&time[2],&t)==3) { //遞推最大時間 memset(dp,0,sizeof(dp)); for(int i=1;i<=2;i++) { for(int j=time[i];j<=t;j++) dp[j] = max(dp[j], dp[j-time[i]]+time[i]); } int tmax=dp[t]; //遞推最大漢堡數目 memset(dp,-1,sizeof(dp)); dp[0]=0; for(int i=1;i<=2;i++) { for(int j=time[i];j<=tmax;j++)if(dp[j-time[i]]!=-1) dp[j] = max(dp[j], dp[j-time[i]]+1); } //輸出結果 printf("%d",dp[tmax]); if(t>tmax) printf(" %d",t-tmax); printf("\n"); } return 0;}
AC代碼2:用方法2一次DP做的
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=10000+5;int n,m,t; //對應題意的含義int time[5];//吃第i種漢堡所花時間struct Node //每個狀態{ int time;//吃漢堡時間 int num; //吃漢堡數目 bool operator<(const Node &rhs)const { return time<rhs.time || (time==rhs.time && num<rhs.num); }}dp[maxn];int main(){ while(scanf("%d%d%d",&time[1],&time[2],&t)==3) { //初始化 memset(dp,0,sizeof(dp)); //遞推 for(int i=1;i<=2;i++) { for(int j=time[i];j<=t;j++) { Node tmp=dp[j-time[i]]; tmp.time += time[i]; tmp.num++; if(dp[j]<tmp) dp[j]=tmp; } } //輸出結果 printf("%d",dp[t].num); if(dp[t].time<t) printf(" %d",t-dp[t].time); printf("\n"); } return 0;}