RUPC E, AOJ-2285: アニペロ (Anipero)
問題概要
A[i] = (e[i], s[i]), B[i] = (ee[i], ss[i])が与えられる(N<100)。AのうちからX以上選んで、Bのうちから1or2個選んでΣee[i] + Σe[i]をLIMIT(<1000)以下になるようにしてΣss[i] + Σs[i]を最大化する問題。
考えたこと
- DP臭がする。あとこれ入力で名前読み取る意味ないよね…。
- シークレットとスタンダード(概要中のA, B)を別個に考えてよい気がする。
- それぞれ、費用いくらのときの最大の満足度を覚えておけば最後にO(LIMIT^2)で全ての組み合わせを計算するだけでよい。
- これで部分問題に落とせた。
- DPで必要な状態は、(何人目、何人既に選んだ、使ったお金)で状態数O(M^2*LIMIT)。遷移は選ぶか選ばないかの2通り。なのでこれで行けそう。
- サクッと書く。MLE!
- 言われてみれば。まあこれはすぐに配列再利用すればいいことに気づく。メモ化で書いてなくてよかった。
- とはいえ10^7*4は40MBでSRMなら通るレベル。この問題は32MBなので落ちた。
- 答えの上限が小さいので、実はshort型にするだけでもよかった。
- 修正。無事AC。
本番で通ったコード
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; const int MAX_LIMIT = 1000; const int MAX_N = 100; const int MAX_X = 100; const int INF = 1<<29; int SEC_ANS[MAX_LIMIT + 1]; int STD_ANS[MAX_LIMIT + 1]; int dp[2][MAX_N+1][MAX_LIMIT+1]; //(何人目、何人選んだ、使ったお金) int LIMIT, X; int E[MAX_N]; int S[MAX_N]; void sub_solve(const int N, int lb, int ub, int ans[MAX_LIMIT + 1]){ //初期化 for(int i=0; i<2; i++){ for(int j=0; j<=MAX_N; j++){ fill(dp[i][j], dp[i][j] + (MAX_LIMIT+1), -INF); } } //基底 dp[0][0][0] = 0; //DP for(int i=0; i<N; i++){ int cur = (i&1), next = 1 - cur; for(int j=0; j<=MAX_N; j++){ fill(dp[next][j], dp[next][j] + (MAX_LIMIT+1), -INF); } for(int j=0; j<=ub; j++){ for(int k=0; k<=LIMIT; k++)if(dp[cur][j][k] >= 0){ //i人目を採用 if(j < ub){ int next_cost = k + E[i]; if(next_cost <= LIMIT){ dp[next][j+1][next_cost] = max(dp[next][j+1][next_cost] , dp[cur][j][k] + S[i]); } } //i人目を不採用 dp[next][j][k] = max(dp[next][j][k], dp[cur][j][k]); } } } //後処理 fill(ans, ans+(LIMIT+1), -INF); for(int l = lb; l<=ub; l++){ for(int i=0; i<=LIMIT; i++){ ans[i] = max(ans[i], dp[(N&1)][l][i]); } } } int solve(){ int ans = 0; for(int i=0; i<=LIMIT; i++){ for(int j=0; j<=LIMIT; j++)if(i + j <= LIMIT){ ans = max(ans, SEC_ANS[i] + STD_ANS[j]); } } return ans; } int main(){ int N, M; while(scanf("%d%d%d%d",&LIMIT, &N, &M, &X), LIMIT){ //シークレット for(int i=0; i<N; i++){ cin.ignore(); string trash; cin >> trash >> E[i] >> S[i]; } sub_solve(N, 1, 2, SEC_ANS); //スタンダード for(int i=0; i<M; i++){ cin.ignore(); string trash; cin >> trash >> E[i] >> S[i]; } sub_solve(M, X, M, STD_ANS); printf("%d\n", solve()); } return 0; }