topcoder-SRM565-div2-第二题-500分--搜索/动态规划
不想看英文的題目?翻譯在后面
Problem Statement | |||||||||||||
| ???? | Manao is traversing a valley inhabited by monsters. During his journey, he will encounter several monsters one by one. The scariness of each monster is a positive integer. Some monsters may be scarier than others. The i-th (0-based index) monster Manao will meet has scariness equal to dread[i]. Manao is not going to fight the monsters. Instead, he will bribe some of them and make them join him. To bribe the i-th monster, Manao needs price[i] gold coins. The monsters are not too greedy, therefore each value in price will be either 1 or 2. At the beginning, Manao travels alone. Each time he meets a monster, he first has the option to bribe it, and then the monster may decide to attack him. A monster will attack Manao if and only if he did not bribe it and its scariness is strictly greater than the total scariness of all monsters in Manao's party. In other words, whenever Manao encounters a monster that would attack him, he has to bribe it. If he encounters a monster that would not attack him, he may either bribe it, or simply walk past the monster. Consider this example: Manao is traversing the valley inhabited by the Dragon, the Hydra and the Killer Rabbit. When he encounters the Dragon, he has no choice but to bribe him, spending 1 gold coin (in each test case, Manao has to bribe the first monster he meets, because when he travels alone, the total scariness of monsters in his party is zero). When they come by the Hydra, Manao can either pass or bribe her. In the end, he needs to get past the Killer Rabbit. If Manao bribed the Hydra, the total scariness of his party exceeds the Rabbit's, so they will pass. Otherwise, the Rabbit has to be bribed for two gold coins. Therefore, the optimal choice is to bribe the Hydra and then to walk past the Killer Rabbit. The total cost of getting through the valley this way is 2 gold coins. You are given the vector <int>s dread and price. Compute the minimum price Manao will pay to safely pass the valley. | ||||||||||||
Definition | |||||||||||||
| ???? |
| ||||||||||||
| ???? | |||||||||||||
| ? | |||||||||||||
Constraints | |||||||||||||
| - | dread will contain between 1 and 20 elements, inclusive. | ||||||||||||
| - | Each element of dread will be between 1 and 2,000,000,000, inclusive. | ||||||||||||
| - | price will contain between the same number of elements as dread. | ||||||||||||
| - | Each element of price will be either 1 or 2. | ||||||||||||
Examples | |||||||||||||
| 0) | ? | ||||||||||||
| ???? |
| ||||||||||||
| 1) | ? | ||||||||||||
| ???? |
| ||||||||||||
| 2) | ? | ||||||||||||
| ???? |
| ||||||||||||
| 3) | ? | ||||||||||||
| ???? |
| ||||||||||||
This problem statement is the exclusive and proprietary property of TopCoder, Inc. Any unauthorized use or reproduction of this information without the prior written consent of TopCoder, Inc. is strictly prohibited. (c)2003, TopCoder, Inc. All rights reserved. ? ??
題目翻譯:
?Manao要走過一片樹林,樹林有怪獸,每個怪獸有一個正整數值的戰斗力,和一個價格(1或2),Manao可以選擇喂養怪獸(花費1或2個金幣),或者直接走過去。Manao也有一個戰斗力值——是他所喂養的怪獸的全體的戰斗力之和。Manao遇到的第i個怪獸戰斗力為dread[i],價格為price[i],如果dread[i]大于Manao當前的戰斗力,Manao就必須喂養它,否則怪獸就會攻擊他,喂養消耗的金幣數為price[i](補充說明:初始Manao戰斗力為0,所以必須喂養第一個怪獸)
求Manao穿過樹林消耗的最少金幣數
輸入限制:
怪獸個數在1~20之間(包含1和20)
怪獸戰斗力在1~2×10^9之間
price的值為1或2
price和dread的包含元素個數相同
題目解析:
?考慮遞歸處理,設當前Manao戰斗力為sum,遇到第idx個怪獸(從0開始編號),設從這個時候開始他最少要消耗f(sum,idx)個金幣才能通過樹林
如果sum>=dread[idx],有兩種選擇,喂養或者直接走過去。如果喂養,付出price[idx]個金幣,同時戰斗力增加到sum+dread[idx],然后就可以處理下一個怪獸了,這種選擇對應的最少消耗金幣數為price[idx] + f(sum + dread[idx], idx + 1);如果不喂養,那么直接處理下一個怪獸,這種選擇對應的最少消耗金幣數為f(sum, idx + 1)。我們只有這兩種選擇,所以f(sum,idx)=min(price[idx] + f(sum + dread[idx], idx + 1),?f(sum, idx + 1))
如果sum<dread[idx], 只有喂養,f(sum, idx) =?price[idx] + f(sum + dread[idx], idx + 1)
遞歸的邊界是:如果idx >= 怪獸個數,f(sum, idx) = 0
(寫著寫著就感覺有點像算法導論動態規劃那一章的裝配線問題)
下面是代碼:
純粹的遞歸搜索:
(這里有個嚴重的效率問題,即宏中使用了函數,應該把define那行注釋掉,具體見http://www.cnblogs.com/fstang/archive/2013/01/07/2849317.html?---補充于2013-01-07)
#include <string> #include <iostream> #include <vector> #include <algorithm> #include <map> using namespace std; #define min(a,b) ((a) < (b) ? (a) : (b))class MonstersValley2{ public:int minimumPrice(vector <int> dread, vector <int> price){return f(0, 0, dread, price);}int f(long long sum, int idx, vector <int> &dread, vector <int> &price){if (idx >= dread.size()) {return 0;}if (sum >= dread[idx]) {int m = min(price[idx] + f(sum + dread[idx], idx + 1, dread, price), f(sum, idx + 1, dread, price)); return m;} else {int m = price[idx] + f(sum + dread[idx], idx + 1, dread, price);return m; }} };//這個代碼有一個超過200ms,有一個超時,其他為0ms或1ms...不能通過
?
帶備忘錄的遞歸搜索(避免重復計算)
(這里同樣有嚴重的效率問題,即宏中使用了函數,應該把define那行注釋掉,具體見http://www.cnblogs.com/fstang/archive/2013/01/07/2849317.html?---補充于2013-01-07)
#include <string> #include <iostream> #include <vector> #include <algorithm> #include <map> using namespace std; #define min(a,b) ((a) < (b) ? (a) : (b)) class Elem{ public:long long sum;int idx;Elem(long long s, int i){sum = s;idx = i;}bool operator < (const Elem &rhs) const {return sum < rhs.sum || sum == rhs.sum && idx < rhs.idx;} };class MonstersValley2{ public:map<Elem, int> S;int minimumPrice(vector <int> dread, vector <int> price){S.clear();return f(0, 0, dread, price);}int f(long long sum, int idx, vector <int> &dread, vector <int> &price){map<Elem, int>::iterator it = S.find(Elem(sum, idx));if (it != S.end()) {return it->second;}if (idx >= dread.size()) {return 0;}if (sum >= dread[idx]) {int m = min(price[idx] + f(sum + dread[idx], idx + 1, dread, price), f(sum, idx + 1, dread, price));S.insert(make_pair(Elem(sum, idx), m)); return m;} else {int m = price[idx] + f(sum + dread[idx], idx + 1, dread, price);S.insert(make_pair(Elem(sum, idx), m));return m; }} };//有5個案例運行時間超過200ms,最長480ms,無超時,多數為0ms或1ms,通過
?寫了個main函數測試一下(要修改類代碼,增加一個cnt成員變量(public),每次從表中查到就cnt++),這個案例就是之前超時的那個,有19個元素,得到結果是:
最小總花費為2,從表中查到的總次數cnt=131071,可見還是有一些重疊子問題的,雖然我感覺sum是很稀疏的,重疊的可能性不大
int main(int argc, char* argv[]) {int a[] = {1782688262, 895047095, 1625373870, 1009836949, 985560038, 1470346827, 296839142, 34727454, 413009041, 1114435639, 1692481802, 422406335, 795130000, 1455087504, 410389760, 961349143, 1693064512, 621415696, 98442513};int b[] = {2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1};MonstersValley2 m;cout << m.minimumPrice(vector<int>(a, a+sizeof(a)/sizeof(int)), vector<int>(b, b+sizeof(b)/sizeof(int))) << endl;cout << "cnt:" << m.cnt << endl;system("pause");return 0; }更細致的測試:統計每個遞歸層次的查表次數,結果如下:
cnt[0]:0
cnt[1]:0
cnt[2]:1
cnt[3]:2
cnt[4]:4
cnt[5]:8
cnt[6]:16
cnt[7]:32
cnt[8]:64
cnt[9]:128
cnt[10]:256
cnt[11]:512
cnt[12]:1024
cnt[13]:2048
cnt[14]:4096
cnt[15]:8192
cnt[16]:16384
cnt[17]:32768
cnt[18]:65536
cnt[19]:0
?竟然都是2的冪,看到這里我也吃了一驚,為什么是這樣?我再想想……
-----------------------------------------------------the following comments are added on 2013-01-04----------------------------------------------------
看到查表時不同元素的查找頻率是不同的,由于map是用紅黑樹實現的,屬于平衡二叉樹,并不是最優的。有個叫最優平衡二叉樹的算法,當每個元素查找頻率不完全相同時,可以構造相應的二叉查找樹,使得總的代價最小。只是想想,因為這個實現起來就復雜了,還要自己寫二叉查找樹……
?
?===============================================補充=============================================
?補充一個Java版本的,因為考慮到hashmap從查找的效率來講比紅黑樹實現的map更好,但是C++11中才提供unordered_map,所以就試試Java,遺憾的是,這個也不夠好,有個case還是fail了,超時,在我的電腦上跑是out of memory....
還是貼一下,main里就是那個fail的case
(感嘆自己對Java很是不熟,寫代碼到處查,各種錯誤,感覺很不好,關于new的用法還老是和C++扯不清)
import java.util.HashMap;public class MonstersValley {private HashMap<Elem, Integer> map = new HashMap<Elem, Integer>();public int minimumPrice(long[] dread, int[] price){return f(0, 0, dread, price);//return 0; }int f(int idx, long sum, long[] dread, int[] price){Integer res;if (idx >= dread.length) {//遞歸邊界return 0;}if (map.containsKey(new Elem(sum, idx))) {//查表return map.get(new Elem(sum, idx));}if (sum >= dread[idx]) {res = Math.min(f(idx + 1, sum, dread, price), price[idx] + f(idx + 1, sum + dread[idx], dread, price)); } else {res = price[idx] + f(idx + 1, sum + dread[idx], dread, price);}map.put(new Elem(sum, idx), res);return res;}public static void main(String args[]){long dread[] = {8589934592l, 4294967296l, 2147483648l, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 170191697543L, 442151420049l, 760562175049l, 13784873423l, 178349965388l, 755005234214l, 541764950940l, 946482302586l, 543713207966l, 762391189316l, 871034340034l, 677925419895l, 160143387182l, 105796128936l, 529757177900l, 289213265278l};int price[] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1};MonstersValley m = new MonstersValley();System.out.println(m.minimumPrice(dread, price));} } class Elem{public long sum;public int idx;public Elem(long s, int i){sum = s; idx = i;} }?
轉載于:https://www.cnblogs.com/fstang/archive/2012/12/23/2829946.html
總結
以上是生活随笔為你收集整理的topcoder-SRM565-div2-第二题-500分--搜索/动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 帧、场编码的个人理解
- 下一篇: Eclipse的ExtJs智能提示