mayoko’s diary

プロコンとかいろいろ。

SRM 503 div1 easy:ToastXToast

SRM練習会に参加。結果はeasyだけ解いてはい。

解法

結構本番不安だったんですがメモ化再帰で解きました。

undertoastedとovertoastedがソートされているとします。

「1種類のパンを考慮するときにundertoastedとovertoastedはどのような関係になっているのか」ということを考えてみます。パンがちょうど美味しく焼ける時間はすべてのundertoastedの時間より長く,すべてのovertoastedの時間よりも短くなければならないという制約があります。よって,以下のようにすれば良いです(めんどくさいのでundertoastedをut, overtoastedをotと書くことにします):


dp[u][o] := (u番目までのutとo番目までのotはすでに確定しているとする。この時,utのu番目からの時間とotのo番目からの時間を使ってパンの種類を考える時,パンの種類の最小数はいくつか)
というdpを考える。これを解くには,

パンを焼く時間を示す整数の組(i, j)(i >= u, j >= o)を考える(ut[u]からut[i]までの時間で焼くとundertoastedになってot[o]からot[j]の時間で焼くとovertoastedになるということ)。この時ut[i] < ot[o]が成り立っていなければならない。これでパン1種類が確定したので次にdp[i+1][j+1]を考える,...というような感じでメモ化再帰を作れば良い。最終的に選ぶのはそれらの整数の組(i, j)のうち最小のもの。

以下ソースコード

int dp[55][55];
int n, m;
const int INF = 1e9;
vector<int> ut, ot;

int dfs(int u, int o) {
    if (u == n && o == m) return 0;
    if (u == n) return INF;
    if (o == m) return INF;
    int& ret = dp[u][o];
    if (ret >= 0) return ret;
    ret = INF;
    for (int i = u; i < n; i++) {
        if (ut[i] > ot[o]) break;
        for (int j = o; j < m; j++) {
            ret = min(ret, 1+dfs(i+1, j+1));
        }
    }
    return ret;
}

class ToastXToast {
public:
    int bake(vector <int> undertoasted, vector <int> overtoasted) {
        ut = undertoasted;
        ot = overtoasted;
        sort(ot.begin(), ot.end());
        sort(ut.begin(), ut.end());
        n = undertoasted.size();
        m = overtoasted.size();
        memset(dp, -1, sizeof(dp));
        int ans = dfs(0, 0);
        if (ans == INF) ans = -1;
        return ans;
    }
};