ACM-ICPC 2010国内予選 E. 最強の呪文
id:kusano_progさんに先に書かれてしまいましたが…。
解法
- [現在の呪文の長さ][現在地] を使った拡張ダイクストラ
- 文字列A, B, Cに対して、一般にmin(A+C, B+C) = min(A, B) + Cは成り立たない
- が,AとBの長さが同じなら成り立つので文字列長ごとに管理する
- エッジの文字列長が高々6なので,ループが無ければ文字列長は(6*ノード)数以下
- 今回は最強の呪文が定まらない場合もあるので(12*ノード数)まで調べた
- ループなしで作れる辞書順最小の文字列をsとする
- ループありでsより辞書順が小さい文字列が作れると最強の呪文は定まらない
- ループ部分の文字列をwとすると,uv > uwv > uwwv > … となるから
- なので、ループありでsより辞書順で小さい文字列を作れるかを調べる必要がある
- ループありでsより辞書順で小さい文字列があれば、倍くらい調べれば1つは見つかる
- 辞書順を小さくするループのうち長さ(6*ノード数)以下のものがある…ハズ
- いろいろ考えましたが、この辺の正当性は自信がないです
- ジャッジデータには反例がありませんでしたが…
- 以下のコードで行っている処理は以下の通り
- 長さ(12*ノード数以下)ごとに拡張ダイクストラで辞書順最小の呪文を調べる
- 作った呪文のうち辞書順最小のものを取りだす
- 呪文を作った経路を調べてループの有無を判定し、答えを出力
7/6 8:24追記 (正当性の証明)
- どうやら(6*ノード数)の倍の長さを調べておけば大丈夫そうです
- 以下では次の証明を試みています。間違いがあったら教えていただけると嬉しいです
- ループなしで作れる辞書順最小の文字列をsとする
- ループありで作れるsより辞書順で小さい文字列が存在するとき、
- そのような文字列のうち長さ(12*ノード数)以下のものが存在する
- 以下では次の証明を試みています。間違いがあったら教えていただけると嬉しいです
- ループありでsより辞書順で小さく、かつsより長い文字列の集合をTとする
- ループなしで辞書順最小の文字列sの長さは(6*ノード数)以下
- 条件からTに属する文字列の先頭|s|文字の辞書順はsより小さい
- Tに属する文字列のうち長さ(12*ノード数)以下のものが存在する
- Tに属する文字列どれかを生成する過程を考える
- 先頭|s|文字を生成したらループを経由せずにゴールに向かうとする
- 実際には|s|〜|s|+5文字を生成したら
- その結果できる文字列をtとすると、tはTに属する
- tの条件からt < s
- tがループを含まないとすると、sの条件に反する
- tの長さを考えると、高々(|s|+5+ゴールまでにできる文字列長)
- ループなしなら生成される文字列の長さは6*(ノード数-1)以下
- 同じノードは2度通らないので通る枝の数は高々(ノード数-1)
- よって、tの長さ ≦ (|s|+5+6*(ノード数-1)) ≦ 12*ノード数
- 上で書いてる「ループの長さが(6*ノード数)以下」は、まだ自信ありません
#include <iostream> #include <string> #include <vector> #include <queue> using namespace std; typedef vector< vector< pair<int, string> > > Graph; class Node{ public: int pos; string str; int prev; Node(int pos, string str, int prev) : pos(pos), str(str), prev(prev) {} bool operator < (const Node &nd) const { return str > nd.str; } }; int main(){ int n, a, s, g; int prev[480][40]; string str[480][40]; while(cin >> n >> a >> s >> g, n){ Graph graph(n); for(int i=0;i<a;i++){ int x, y; string lab; cin >> x >> y >> lab; graph[x].push_back(make_pair(y,lab)); } memset(prev, -1, sizeof(prev)); for(int i=0;i<12*n;i++) for(int j=0;j<n;j++) str[i][j] = ""; priority_queue<Node> qu; qu.push(Node(s, "", -2)); while(!qu.empty()){ Node nd = qu.top(); qu.pop(); int pos = nd.pos, size = nd.str.size(); if(prev[size][pos]!=-1) continue; prev[size][pos] = nd.prev; str[size][pos] = nd.str; for(int i=0;i<graph[pos].size();i++){ int npos = graph[pos][i].first; if(size + graph[pos][i].second.size() >= 12*n) continue; string nstr = nd.str + graph[pos][i].second; if(str[nstr.size()][npos]==""||nstr<str[nstr.size()][npos]){ str[nstr.size()][npos] = nstr; qu.push(Node(npos, nstr, size*n+pos)); } } } int minpos = -1; string ans = ""; for(int i=0;i<12*n;i++){ if(str[i][g]=="") continue; if(ans == ""||ans > str[i][g]){ ans = str[i][g]; minpos = i; } } bool flag = (minpos != -1); bool visit[40]; memset(visit, false, sizeof(visit)); int cur = minpos*n+g; while(flag&&cur!=-2){ if(visit[cur%n]) flag = false; visit[cur%n] = true; cur = prev[cur/n][cur%n]; } cout << (flag ? ans : "NO") << endl; } }