【精進9】ARC030-C 有向グラフ
【精進9】ARC030-C 有向グラフ
結構面倒だった
問題概要
C - 有向グラフ
$N$頂点$M$辺の有向グラフが与えられる。各頂点には英小文字が書かれている。好きな頂点から開始し、英小文字を回収するか選び移動することを繰り返すとき長さ$K$の得られる文字列の内、辞書順最小のものを求めよ。
解法
強連結な集合内は自由に行き来できるから、文字を小さい順に貪欲に回収できる。従って強連結成分分解することで各頂点に(文字ではなく)文字列が書かれたDAGの問題に帰着できる。各頂点には元のグラフの強連結成分の頂点に書かれた文字をソートして並べた文字列が書かれているとする。$\mathrm{DP}[i][j]$ : 「頂点$i$及びより上流(用語がわからない)の頂点を使ってできる長さ$j$の文字列の内、辞書順最小のもの」としてDPをすれば解ける。
ところでchmin
を文字列で初めて使ったかもしれない。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include "template.hpp"
#include "data_structure/hash-map-variable-length.hpp"
#include <atcoder/scc>
void solve() {
LL(n, m, k);
vector<char> c(n);
input(c);
atcoder::scc_graph g(n);
vector<P> edge(m);
rep(i, m) {
LL(a, b);
g.add_edge(--a, --b);
edge[i] = {a, b};
}
auto scc = g.scc();
ll sz = scc.size();
HashMap<ll> mp;
vector<string> s(sz);
rep(i, sz) {
for(auto &j : scc[i]) {
mp[j] = i;
s[i].push_back(c[j]);
}
}
Graph gs(sz);
for(const auto &[i, j] : edge) {
if(mp[j] != mp[i])
gs[mp[j]].push_back(mp[i]);
}
rep(i, sz) {
sort(all(gs[i]));
gs[i].erase(unique(all(gs[i])), end(gs[i]));
sort(all(s[i]));
}
vector<vector<string>> dp;
string ans;
char z = '{';
{
ans = string(k, z);
vector<string> dp0(k + 1);
rep(i, k + 1) {
dp0[i] = string(i, z);
}
dp.resize(sz, dp0);
}
rep(i, sz) {
vector<string> pdp(k + 1);
rep(j, k + 1) pdp[j] = string(j, z);
for(auto prev : gs[i]) {
rep(j, k + 1) {
chmin(pdp[j], dp[prev][j]);
}
}
auto &str = s[i];
rep(j, k + 1) {
rep(l, min(j, ssize(str)) + 1) {
chmin(dp[i][j], pdp[j - l] + str.substr(0, l));
}
}
chmin(ans, dp[i][k]);
}
if(ans[0] == z) {
print(-1);
} else
print(ans);
}
int main() {
ios::sync_with_stdio(false);
std::cin.tie(nullptr);
cout << std::setprecision(16);
int t = 1;
rep(_, t) {
solve();
}
}
This post is licensed under CC BY 4.0 by the author.