TopCoder SRM 563 Div1 Medium: SpellCards
解法
配列が環状になっている区間のDP。「first枚目からcount枚の連続した区間の中からmustLeave枚取り除かなければならない場合の最高点」を考える。
コード
Petrのコードを参考にした。
import java.util.Arrays; public class SpellCards { int[] level; int[] damage; int[][][] cache; int N; public int maxDamage(int[] level, int[] damage) { N = level.length; this.level = level; this.damage = damage; for (int i = 0; i < N; ++i) --level[i]; cache = new int[N][N][N]; for (int[][] a : cache) for (int[] b : a) Arrays.fill(b, -1); int ans = 0; for (int lastPlayCard = 0; lastPlayCard < N; ++lastPlayCard) { int first = (lastPlayCard + 1) % N; int max = dfs(first, N - 1, level[lastPlayCard]); if (max >= 0) { ans = Math.max(ans, max + damage[lastPlayCard]); } } return ans; } // firstからcount枚の区間からmustLeave枚取り除かなければならない時の最高スコアを返す private int dfs(int first, int count, int mustLeave) { if (mustLeave > count) return -1; if (cache[first][count][mustLeave] >= 0) return cache[first][count][mustLeave]; int res = 0; for (int headCount = 0; headCount < count; ++headCount) { // curのカードを使うことを考える int cur = (first + headCount) % N; int tailCount = count - 1 - headCount;// curの後ろにあるカードの枚数 if (level[cur] > tailCount) continue; for (int leave = level[cur]; leave <= tailCount; ++leave) { int restLeave = mustLeave + level[cur] - leave; if (restLeave < 0) break; // curより後ろの区間 int nextFirst = (first + headCount + 1) % N; int tailMax = dfs(nextFirst, tailCount, leave); if (tailMax < 0) continue; // curよりも前の区間 int headMax = dfs(first, headCount, restLeave); if (headMax < 0) continue; res = Math.max(res, headMax + tailMax + damage[cur]); } } cache[first][count][mustLeave] = res; return res; } }