mayoko’s diary

プロコンとかいろいろ。

AOJ 2190:Angel Stairs

解法

後ろからたどっていけば良いです。
例えば, ひとつ目の入力例を見ていきましょう。

C E D# F G A
C E F G

まずそれぞれの配列を逆さまにします。

A G F D# E C
G F E C

まず G を作る必要があります。スタートが A だとどのように半音ずらしても G にはならないので最初(実際には最後)に踏む階段は A ではないです。
一方で, スタートが G の場合, G を踏むときに一段降りてきたと考えれば G という音を出せます。よって, G の前に踏んでいた階段は G の一つ上である F であるとわかります。
次に作る必要のある音は F です。これも先ほどと同様に一段降りてきたと考えれば良いので F の前に踏んでいた階段は D# です。
次に作る必要のある音は E です。今度は一段とばしで降りてきたと考えると E の音が出るので, D# の前に踏んでいた階段は C です。で, C を出すために一段降りて雲に着く, というわけです。

これを上手く実装すれば良いです。

const int MAXN = 50040;
int T[MAXN], S[MAXN];

bool check(int start, int n, int m) {
    int now = start;
    for (int i = 0; i < m; i++) {
        if (now > n-1) return false;
        int tar = S[i];
        bool ok = false;
        for (int j = -1; j <= 1; j++) {
            int next = (T[now]+j + 12) % 12;
            if (tar == next) {
                ok = true;
                now += j;
                if (j >= 0) now++;
                break;
            }
        }
        if (now < 0) return false;
        if (!ok) return false;
    }
    if (now == n) return true;
    else return false;
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int test;
    cin >> test;
    map<string, int> oto;
    oto["C"] = 0;
    oto["C#"] = 1;
    oto["D"] = 2;
    oto["D#"] = 3;
    oto["E"] = 4;
    oto["F"] = 5;
    oto["F#"] = 6;
    oto["G"] = 7;
    oto["G#"] = 8;
    oto["A"] = 9;
    oto["A#"] = 10;
    oto["B"] = 11;
    while (test--) {
        int n, m;
        cin >> n >> m;
        for (int i = 0; i < n; i++) {
            string s;
            cin >> s;
            T[i] = oto[s];
        }
        for (int i = 0; i < m; i++) {
            string s;
            cin >> s;
            S[i] = oto[s];
        }
        reverse(S, S+m);
        reverse(T, T+n);
        if (check(0, n, m) || check(1, n, m)) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    return 0;
}