HLUOJ蛋糕塔(USACO乳酪塔)
USACO乳酪塔
對於這道題呢,是DP,確切地說是揹包,還是完全揹包,今天早上也就只打了這題,因為有位童鞋一直在“不恥下問”,讓我對這題逐步有了深刻的理解 {(¬_¬)智商三歲},於是認為一定要來水一下部落格。
這個乳酪塔在我們的 “偶劇誒”(OJ)成功地發酵變成了“蛋糕塔”,老劉想掩人耳目是不可能的,因為這道題兩位大佬都給我們講過,而且檔案輸入輸出還是“cheese”,更坑的是還多帶一個點!!這是坑了多少人 經過我的瘋狂吐槽後,我們就不在追究這波操作了。進入正題:
題目描述(原諒我的複製貼上) Hl高中要舉行一場蛋糕塔比賽。(有的吃嗎?ヾ(◍°∇°◍)ノ゙)
學校會提供N種不同型別的蛋糕,第i種蛋糕的高度為Hi
蛋糕塔比賽的規則就是要求按照提供的蛋糕,壘成一個高度不超過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 有3種蛋糕,蛋糕塔的高度限制為53,高度必須超過25的蛋糕才能將其下面的蛋糕高度壓縮。我們按照下面的方法來壘蛋糕:
個數 高度 價值 高->[1] 25 100 [2] 4 20 [3] 8 40 [3] 8 40 底->[3] 8 40
最上的蛋糕將下方的蛋糕都壓縮為4/5.最大價值為240
解: 非常幸運的,我們第一眼就發現這是一個完全揹包再進行一小點操作,而且一系列的一系列都和蛋糕的高度扯上了關係,因此我們設f[i]表示蛋糕塔高度為i時的最優值,但是由於塔的高度會被大蛋糕所壓縮,因此改進表示為未被壓縮的高度(即實際高度)為i的蛋糕塔的最高營養價值。正因為是未被壓縮過的,又因為壓縮時會變成實際的4/5,所以塔的實際最高 高度為t/(4/5)即t*5/4
接下來,我們看一看完全揹包的模板:
for (int i = 1; i <= n; ++i) {
for (int j = w[i]; j <= v; ++j) {
f[j] = max(f[j], f[j - c[i]] + v[i]);
}
}
一系列過程參考:https://www.cnblogs.com/Kalix/p/7622102.html
為了好理解我們將遺忘
既然f[i]記錄的是當蛋糕塔實際高度為i時的最優營養值,對於當下的小蛋糕(i 這個i不同於f[i]的i)來說,可以放一個或放n個知道放滿,即高度為t(這是對於壓縮後來說的)那麼實際上是可以放 這個小蛋糕的高度~t/(4/5) 因此完全揹包的內迴圈確定為:for(int j=h[i];j<=t*5/4;j++)
依次更新蛋糕塔高度為j時的最優值。
溫故而知新:f[j]=max(f[j],f[j-h[i]]+v[i]);
max中f[j] 表示不叫當前的蛋糕,f[j-h[i]] 表示在到達指定高度的前提下,放當下這塊蛋糕的最優值,整個f[j-h[i]]+v[i] 表示放下這塊蛋糕的最優值
ans用來記錄答案即最後最優值f[t]
但是問題來了f[]不是用來表示實際高度的嗎?最大值不應該是f[t5/4]嗎?為什麼會是f[t]呢?
在這裡需要說明一下:f[]在此時表示的是一堆小蛋糕疊在一起的實際高度的最優值,但是也不能排除小蛋糕剛好搭到t,大蛋糕就不能進行壓縮,因此實際高度=名義上的壓縮高度。
最後就是傳說中的一點點小操作了,用大蛋糕進行壓縮,更新ans。因為要求最優值,所以我們不能讓蛋糕塔空出位置不利用,因此確定壓縮後的高度 一定為t,那麼除去大蛋糕的高度,被壓縮的小蛋糕的高度就為t-h[i],又因為f[]記錄的是實際高度而不是壓縮高度,壓縮高度又可以以5/4轉換為實際高度,所以ans=max(ans,f[(t-h[i])*5/4]+v[i]);
那麼接下來,上程式碼:
//要麼最優都是小cake,要麼就是有一個大cake是在頂上的
//對於只有小cake直接用完全揹包來做
//最後在頂端放大cake對ans進行更新
#include<bits/stdc++.h>
using namespace std;
int v[5050],h[5050];
int f[5050];
int main()
{
freopen("cheese..in","r",stdin);
freopen("cheese..out","w",stdout);
int n,t,k;
cin>>n>>t>>k;
for(int i=1;i<=n;i++)
cin>>v[i]>>h[i];
for(int i=1;i<=n;i++)
for(int j=h[i];j<=t*5/4;j++)
//對於高度來說,因為當下只考慮了小cake,
//所以這個高度是實際,即目的高度的4/5
//因此/(4/5)即*5/4
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;
}