【習題詳解】動態規劃DP:硬幣遊戲 蛋糕
動態規劃DP
硬幣
題目描述 農夫約翰的奶牛喜歡玩硬幣遊戲,因此他發明了一種稱為“Xoinc”的兩人硬幣遊戲。 初始時,一個有N(5 <= N <= 2,000)枚硬幣的堆疊放在地上,從堆頂數起的第I枚硬幣的幣值為C_i (1 <= C_i <= 100,000)。 開始玩遊戲時,第一個玩家可以從堆頂拿走一枚或兩枚硬幣。如果第一個玩家只拿走堆頂的一枚硬幣,那麼第二個玩家可以拿走隨後的一枚或兩枚硬幣。如果第一個玩家拿走兩枚硬幣,則第二個玩家可以拿走1,2,3,或4枚硬幣。在每一輪中,當前的玩家至少拿走一枚硬幣,至多拿走對手上一次所拿硬幣數量的兩倍。當沒有硬幣可拿時,遊戲結束。 兩個玩家都希望拿到最多錢數的硬幣。請問,當遊戲結束時,第一個玩家最多能拿多少錢呢? 輸入格式
好難啊…看了我0.5h的題解才看懂的… 這道題目我們可以選擇倒著推:因為最後一個因此必然是要取完的,因此可以確定狀態一定是可行的。 我們給每一個金幣自底向上標號為1-i(這裡自然需要逆序讀入) 狀態:設f[i][j]表示某一方取完以後還剩下編號為1-i的金幣,上一個人取了j枚金幣的最大取值(顯然上一個人取的金幣編號一定大於i)。 轉移:對於每一個狀態我們必然需要知道一個當前所取的金幣數k,必然和f[i-k][]有關,表示1-i堆中取了k,還剩下1~i-k枚金幣;同樣,也和f[][k]有關,前一個人轉移到這個人的時候,當前取的是k,那麼必然就是k;那麼這個人當前取的金幣數就是總金幣減去下一個決策的金幣(這裡用sum[]記錄金幣的字首和),可得狀態轉移方程: 優化:觀察每一個f[i][j],對於每一個f[i][j-1],多餘的決策只有選2j-1枚金幣和2j枚,只要k等於這兩個值做兩遍轉移,和原來求最大值取一個max即可。
#include<bits/stdc++.h>
using namespace std;
#define maxn 3000
int a[maxn];
int sum[maxn];
int f[maxn][maxn];
//表示其中的一個人,拿金幣的時候剩下了1-i,上一個人拿了j個金幣的最大值
inline void read(int &readnum)
{
int s=0,w=1;char c=getchar();
while (c<'0' || c>'9') {if (c=='-') w=-1;c=getchar();}
while (c>='0' && c<='9') s=s*10+c-48,c=getchar();
readnum=s*w;
}
int main(void)
{
freopen("xoinc..in","r",stdin);
freopen("xoinc..out","w",stdout);
int n;
read(n);
for (int i=n;i;--i) read(a[i]);
for (int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
{
f[i][j]=f[i][j-1];
int k=2*j-1;
if (i>=k) f[i][j]=max(f[i][j],sum[i]-f[i-k][k]);
k=2*j;
if (i>=k) f[i][j]=max(f[i][j],sum[i]-f[i-k][k]);
}
cout<<f[n][1];
return 0;
}
蛋糕塔
題目描述 Hl高中要舉行一場蛋糕塔比賽。注意,不是蛋糕比賽,而是蛋糕塔比賽。 學校會提供N種不同型別的蛋糕,第i種蛋糕的高度為Hi(5 <= H_i <= T),營養價值為Vi(1 <= Vi<= 1,000,000),並且保證所有蛋糕的高度為5的整數倍,每種型別的蛋糕沒有數量限制。 蛋糕塔比賽的規則就是要求按照提供的蛋糕,壘成一個高度不超過T(1 <= T <= 1,000)的蛋糕塔,並且要求這個蛋糕塔所有蛋糕的營養價值累加和最高。 因為蛋糕不是很結實,參加比賽的小x發現一個現象,如果某塊蛋糕的高度超過K,那麼這塊蛋糕下面的所有蛋糕的高度都將被壓縮為自己高度的4/5,但是營養價值不會丟失。發現這個情況後的小x很興奮,現在他想知道,如何安排自己的蛋糕塔,能讓營養價值最高。 輸入格式 第一行三個整數:N,T和K; 接下來N行,每行兩個整數:Vi 和 Hi。 輸出格式 一行,一個整數,表示答案。 樣例資料 input 3 53 25 100 25 20 5 40 10 output 240
這道題完全屬於一個完全揹包的變形。對於不會完全揹包可以戳這裡,這裡不再多講。 對於這道題,一共有兩種情況: 1.不用大蛋糕–>用小蛋糕做一次完全揹包即可。 2.用大蛋糕–>把所有蛋糕壓成五分之四做一遍完全揹包得到陣列f[i](其含義代表體積為i的揹包所能容下的最大價值),對於每一個大蛋糕,答案就是f[t-h[i]]. 程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define maxn 10000
#define maxT 120000
int n,t,k;
int h[maxn];
int v[maxn];
int f[maxT];
inline void read(int &readnum)
{
int s=0,w=1;char c=getchar();
while (c<'0' || c>'9') {if (c=='-') w=-1;c=getchar();}
while (c>='0' && c<='9') s=s*10+c-48,c=getchar();
readnum=s*w;
}
int main(void)
{
freopen("cheese..in","r",stdin);
freopen("cheese..out","w",stdout);
read(n);
read(t);
read(k);
for (int i=1;i<=n;++i) read(v[i]),read(h[i]);
for (int i=1;i<=n;++i)
for (int j=h[i];j<=t*5/4;++j)
f[j]=max(f[j],f[j-h[i]]+v[i]);
int ans=f[t];
for (int i=1;i<=n;++i)
if (h[i]>=k) ans=max(ans,f[(t-h[i])*5/4]+v[i]);
cout<<ans<<endl;
return 0;
}