SRM308 div2 difficult [典型的なナップサック問題]
#define TR_MAX 50 class TreasuresPacking { private: typedef struct _things { int weight; int cost; bool flex; } Things; public: double maximizeCost(vectortreasures, int W) { int sz = treasures.size(); vector tr(sz); for(int i = 0; i < sz; i++) { char ch; sscanf(treasures[i].c_str(), "%d %d %c", &tr[i].weight, &tr[i].cost, &ch); tr[i].flex = (ch == 'Y') ? true : false; } double dp[TR_MAX+1][W+1]; //dp[cnt+1][weight+1] memset(dp, 0, sizeof dp); for(int i = 0; i < sz; i++) { for(int j = 0; j < W; j++) { int tmpW = tr[i].weight; //わりとここらへんがテクニック的だと思う. //k = 0つまり、加えないときもループしているので、かならず一回は青色を通る. for(int k = 0; k <= tmpW; k++) { if(tr[i].flex == false) { if(k >= 1) k = tmpW; } if(j+k <= W) { dp[i+1][j + k] = max(dp[i][j] + tr[i].cost * k*1.0 /tmpW, dp[i+1][j+k]); } else { break; //dp[i+1][j] = max(dp[i][j], dp[i+1][j])とかいらない. } } } //end for return *max_element(dp[sz], dp[sz]+W+1); } //でも多分最悪の状態になったとき、時間が足りなくなりそう. (i:50)*(j:10000)*(k:10000) 50億になっちゃう.
ここで、Wは1~10000とかなり範囲が広いので、配列のほとんどが使われない可能性がある.
そこで,mapをつかってdpをくむ。
そして、三重ループを2重ループになんとかしたいわけだが、普通ものをえらぶとき、コスパの高い方から順に選ぶ(どん欲法)ので、並び替えてdpを使うと良さそうということがわかる。
class TreasuresPacking { private: typedef struct _things { int weight; int cost; bool flex; } Things; <span class="deco" style="color:#FF0000;">//クラスは入れ子にできるよ.</span> class cmp { public: //コスパの高い順に並べる. bool operator() (const Things& left, const Things& right) { return ((left.cost)*1.0/left.weight) > ((right.cost)*1.0/right.weight); } }; public: double maximizeCost(vector <string> treasures, int W) { int sz = treasures.size(); vector<Things> tr(sz); //INPUT for(int i = 0; i < sz; i++) { char ch; sscanf(treasures[i].c_str(), "%d %d %c", &tr[i].weight, &tr[i].cost, &ch); tr[i].flex = (ch == 'Y') ? true : false; } sort(tr.begin(), tr.end(), cmp() ); map<int, double> dp[sz+1]; map<int, double>::iterator it; //trigger dp[0][0] = 0; //Dynamic Programming for(int i = 0; i < sz; i++) { for(it = dp[i].begin(); it != dp[i].end(); it++) { int j = it->first; int rem = W - j; if(rem >= tr[i].weight) { rem = tr[i].weight; } else if(rem >= 0 && rem < tr[i].weight) { rem = (tr[i].flex) ? rem : 0; } //dp[i+1][j+rem]は存在しない可能性があるので、if(dp[i+1].equal_range(j+rem) == dp[i+1].end() ) //に応じて条件分岐しても良いが.そこまでこったことはしなくてよいだろうと判断. dp[i+1][j+rem] = max(dp[i][j] + (tr[i].cost)*rem*1.0/tr[i].weight, dp[i+1][j+rem]); } } double ans = 0; for(it = dp[sz].begin(); it != dp[sz].end(); it++) { ans = max(it->second, ans); } return ans; } //これで時間制限にひっかかることはないだろう. おそらく求められているのは、mapを使うことではなくて、二重ループにすることだろう.