TJOI 2013 拯救小矮人

2022-03-30 01:43:11 字數 1623 閱讀 2734

\(\\\)

有\(n\)個人,每個人有兩個屬性 \(a_i,b_i\) ,現在他們要從洞裡逃出去。

第 \(x\) 個人能逃出去的條件是,隨意選擇一些還在洞裡的人\((\)重新編號為\(1...k)\),滿足

\[a_1+a_2+...+a_k+a_x+b_x\ge h

\]注意,第 \(x\) 個人不能在這 \(k\) 個人裡。求最多能跑出去多少個人。

\(\\\)

貪心和 \(dp\) 的結合,不得不說是一道好題。

首先考慮能否直接貪心。

考慮如果直接按照 \(a\) 從大到小排個序,然後強制留下來前面的一些使得滿足出洞條件,然後讓其他的人都出去是否是最優解。

並不是。如果把 \(a\) 比作身高,把 \(b\) 比作手長,那可能有一些身子又高手又長的人,用一些身子一般高但是手極短的人換掉,可能能逃跑的人更多。

\(\\\)

然後就有了比較厲害的 \(dp​\) 解法。

首先考慮兩個人 \(x,y\) 他們如果要出洞,應該以什麼順序出。

答案是 \(a+b\) 較小的那個人先出。

證明分三種情況討論。

然後就可以按照 \(a+b\) 排序了。

\(\\\)

然後設狀態 \(f[i]\) 表示出去了 \(i\) 個人,剩下的人的身高之和最多是多少。

然後狀態之間的轉移就代表出去了乙個人。我們要保證轉移合法,一定要滿足:

然後解決這個問題的方法就比較妙了。

我們顯然不能在外層列舉出去的人數,然後內層列舉出去的人,這樣兩個問題都無法避免。

採取一種類似揹包的思路。外層列舉出去的人,內層列舉要更新的層數。

為了滿足第乙個限制條件,我們要像 \(01\) 揹包那樣更新,保證乙個人只被使用一次。

為了滿足第二個限制條件,我們要動態控制狀態的上限。

\(\\\)

先放**。

f[ans=0]=sum;

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

外層迴圈是考慮哪個人出去,內層迴圈是要更新的層數,內層倒序的原理上面說過了。

轉移條件挺顯然的吧,就是合法就把他拿出去,然後嘗試更新。

為什麼一定能拿出去?因為這個位置有值,一定是從更低的轉移過來,而更低的拿出去的只是當前這個人之前的人,注意體會這個 \(ans\) 的作用。

\(\\\)

#include#include#include#include#include#include#include#define n 2010

#define r register

#define gc getchar

#define inf 2000000000

using namespace std;

inline int rd()

while(isdigit(c))

return f?-x:x;

}int n,m,ans,f[n];

struct prsp[n];

inline bool cmp(prs x,prs y)

printf("%d\n",ans);

return 0;

}

TJOI2013 拯救小矮人

一群小矮人掉進了乙個很深的陷阱裡,由於太矮爬不上來,於是他們決定搭乙個人梯。即 乙個小矮人站在另一小矮人的 肩膀上,知道最頂端的小矮人伸直胳膊可以碰到陷阱口。對於每乙個小矮人,我們知道他從腳到肩膀的高度ai,並且他的胳膊長度為bi。陷阱深度為h。如果我 們利用矮人1,矮人2,矮人3,矮人k搭乙個梯子...

TJOI2013 拯救小矮人 排序 dp

題目鏈結 imagine的完美回答 重點大概是證明我們選出要救的小矮人一定可以根據 a i b i 的大小進行排序救出。注意這裡關注的物件是可以保留的高度,所以我們的dp值才會表示成最少減少的高度。includeusing namespace std define go u for int i he...

TJOI2013 松鼠聚會

題目描述 草原上住著一群小松鼠,每個小松鼠都有乙個家。時間長了,大家覺得應該聚一聚。但是草原非常大,松鼠們都很頭疼應該在誰家聚會才最合理。每個小松鼠的家可以用乙個點x,y表示,兩個點的距離定義為點 x,y 和它周圍的8個點 x 1,y x 1,y x,y 1 x,y 1 x 1,y 1 x 1,y ...