SRM 232 DIV1 Hard SalesPromotion
問題
商品がいくつかある。
商品にはそれぞれ"pointValue"と値段がある。
ポイントをその商品のpointValueだけ使ってその商品を無料で買うことができる。
また、半額ポイントというものもあって、それを1つ使って1つの商品を半額で買える。
ポイントを使わなくても値段からdiscountパーセントの値引きで買うことができる。
半額と値引きの値段は全て切り上げて計算される。
ポイント・半額ポイント・値引きは重複させることができない。
今ポイントをpoints、半額ポイントをhalfPrice持っていて、itemsを全て買いたい。
また、ポイントと半額ポイントは全て使いきりたい。
このとき、支払いを最小化せよ
- 1 <= |items| <= 50
- 1 <= 商品の値段 <= 99999
- 1 <= 商品のpointValue <= 9999
- 0 <= points <= 15000
- 0 <= halfPrice <= |items|
- 0 <= discount <= 49
- ポイントと半額ポイントを使い切る方法が1つ以上存在する
解答
単純なDP。
(何番目の商品まで見て * 残りポイント * 残り半額ポイント)
メモリを節約するために2つだけ覚える。1つだけ覚えてもできる
コメント
"round up"を見逃していてFailed。
整数の割り算・パーセンテージでは丸め方向がどちらなのか、絶対に原文をきちんと読んで確認しよう
コード
#include <string> #include <vector> #include <sstream> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i)) #define INF 0x3f3f3f3f using namespace std; int pointValue[55], priceHalf[55], priceDiscount[55]; int dp[2][15001][51]; struct SalesPromotion { int n; int bestPrice(int points, int halfPrice, int discount, vector <string> items) { n = items.size(); rep(i, n) { stringstream ss(items[i]); int v, p; ss >> v >> p; pointValue[i] = v; priceHalf[i] = (p + 1) / 2; priceDiscount[i] = (p * (100 - discount) + 99) / 100; } for(int i = n; i >= 0; i --) rer(p, 0, points) rer(h, 0, halfPrice) { int r = INF; if(i == n) { if(p == 0 && h == 0) r = 0; }else { if(p >= pointValue[i]) r = min(r, dp[(i+1)&1][p-pointValue[i]][h]); if(h) r = min(r, priceHalf[i] + dp[(i+1)&1][p][h-1]); r = min(r, priceDiscount[i] + dp[(i+1)&1][p][h]); } dp[i&1][p][h] = r; } return dp[0][points][halfPrice]; } };