RUPC2019参加記 Day2
1日目の続きです。
※この記事は問題のネタバレを含みます
コンテスト
@prd_xxxさん、@cider68760155さん、@medalotteさんと組みました。
問題は、会津大学の5時間のセットでした。
この日は振り返ってみると結構たくさん解いていたようです。
コンテスト中に合計3個の誤読をしましたが、チームメイトに指摘されたりして全て修正できてよかったです。CDEGとHの考察をやりました
— けんしん (@knshnb) March 6, 2019
C: {s[i], s[i+1]}の数をカウントしておいて9!全探索
D: yの整数条件を読み飛ばしてずっと相加相乗してた、整数なのでdpでよい
E: ダイクストラ
G: 全部1引いてmodとってNIM
H: 問題を読むと燃やす埋めるという単語が浮かんできたのでググると、いいスライドが見つかった
内容
C: Phone Number
ICPCにありそうな(?)全探索。
signed main() {
int n; cin >> n;
string ss; cin >> ss;
vector<int> s(n); REP (i, n) s[i] = ss[i] - '1';
vector<vector<int>> a(9, vector<int>(9));
REP (i, n - 1) {
int x = s[i], y = s[i + 1];
if (x > y) swap(x, y);
a[x][y]++;
}
vector<int> va;
int ans = 1e18;
vector<int> t(9); REP (i, 9) t[i] = i;
do {
int acc = 0;
REP (i, 9) {
REPI (j, i + 1, 9) {
int x = t[i], y = t[j];
if (x > y) swap(x, y);
int dis = abs(i % 3 - j % 3) + abs(i / 3 - j / 3);
acc += a[x][y] * dis;
}
}
if (chmin(ans, acc)) {
va = t;
}
} while (next_permutation(ALL(t)));
REP (i, 3) {
REP (j, 3) {
cout << va[3 * i + j] + 1;
}
cout << endl;
}
}
D: Tunnel
DはDPのD。
double eps = 1e-5;
double get(double a, double b) {
if (a + b < eps) return 0;
return (a * a + b * b) / (2.0 * (a + b));
}
signed main() {
int n; cin >> n;
vector<double> h(n); REP (i, n) cin >> h[i];
const int K = 301;
vector<vector<double>> dp(n + 1, vector<double>(K, 1e18));
dp[0][1] = 0;
REP (i, n) {
REPI (k1, 1, K) {
REPI (k2, 1, K) {
double ad = dp[i][k1];
if ((k1 < h[i] && k2 < h[i]) || (k1 > h[i] && k2 > h[i])) {
ad += (abs(h[i] - (double)k1) + abs(h[i] - (double)k2)) / 2.0;
} else {
ad += get(abs(h[i] - (double)k1), abs(h[i] - (double)k2));
}
chmin(dp[i + 1][k2], ad);
}
}
}
double ans = 1e18;
REPI (k, 1, K) chmin(ans, dp[n][k]);
cout << setprecision(10) << ans << endl;
}
E: こたつがめを燃やさないで
辺を工夫して貼るとダイクストラに帰着されます。
周囲8方向全てに辺を貼ってしまえば、他の地点では爆破の影響を考えなくて良くなることがポイント。
vector<int> dx = {-1, 0, 1, 0};
vector<int> dy = {0, -1, 0, 1};
vector<int> ex = {-1, -1, 1, 1};
vector<int> ey = {-1, 1, -1, 1};
signed main() {
int h, w, a, b; cin >> h >> w >> a >> b;
vector<string> t(h); REP (i, h) cin >> t[i];
ii s, g;
REP (i, h) {
REP (j, w) {
if (t[i][j] == 's') s = {i, j};
if (t[i][j] == 'g') g = {i, j};
}
}
auto valid = [&](ii p) {
return 0 <= p.fi && p.fi < h && 0 <= p.se && p.se < w;
};
auto around_bomb = [&](ii p) {
bool ret = false;
ret |= t[p.fi][p.se] == '*';
REP (i, 4) {
ii to = {p.fi + dy[i], p.se + dx[i]};
if (valid(to)) ret |= t[to.fi][to.se] == '*';
}
REP (i, 4) {
ii to = {p.fi + ey[i], p.se + ex[i]};
if (valid(to)) ret |= t[to.fi][to.se] == '*';
}
return ret;
};
vector<vector<int>> d(h, vector<int>(w, 1e17));
d[s.fi][s.se] = 0;
priority_queue<pair<int, ii>, vector<pair<int, ii>>, greater<pair<int, ii>>> q;
q.push({0, {s}});
while (!q.empty()) {
pair<int, ii> pii = q.top(); q.pop();
ii p = pii.se;
if (d[p.fi][p.se] < pii.fi) continue;
bool bomb = around_bomb(p);
REP (i, 4) {
ii to = {p.fi + dy[i], p.se + dx[i]};
if (!valid(to)) continue;
int tmp = d[p.fi][p.se] + a;
if (t[to.fi][to.se] == '#') {
if (bomb) continue;
tmp += b;
}
if (chmin(d[to.fi][to.se], tmp)) {
q.push({tmp, to});
}
}
REP (i, 4) {
ii to = {p.fi + ey[i], p.se + ex[i]};
if (!valid(to)) continue;
int tmp = d[p.fi][p.se] + 2 * a;
if (t[to.fi][to.se] == '#' || (t[p.fi + ey[i]][p.se] == '#' && t[p.fi][p.se + ex[i]])) {
if (bomb) continue;
tmp += b;
}
if (chmin(d[to.fi][to.se], tmp)) {
q.push({tmp, to});
}
}
}
if (d[g.fi][g.se] == 1e17) {
cout << "INF" << endl;
} else {
cout << d[g.fi][g.se] << endl;
}
}
G: First Kiss
「どれか1つ」という条件の逆型のゲームは、1を引けば「全部」という条件の正型に帰着されるんですね。
あと、個数d個制限のNIMはmod (d + 1)を考えればよいです。
signed main() {
int n, D; cin >> n >> D;
int acc = 0;
REP (i, n) {
int a; cin >> a;
acc ^= (a - 1) % (D + 1);
}
cout << (acc ? "First" : "Second") << endl;
}
H: Ghost
燃やす埋めるを知らなかったのですが、フローであることを問題文からエスパーしてググり力で解きました。
燃やす埋めるについてはnicoshindanninさんのスライドが分かりやすかったです。
@prd_xxxさんに実装してもらいました。
個人的には、デバッグを手伝っているときに@prd_xxxさんのPythonのDinic法ライブラリのバグを目で発見したのがハイライトでした。
コンテスト後に自分で書いたものを張っておきます。
// Max Flowは省略
signed main() {
int n, m; cin >> n >> m;
string u; cin >> u;
DirectedGraph g(n + 2);
int s = n, t = n + 1;
REP (i, n) {
int a; cin >> a;
if (u[i] == 'R') {
g.add_edge(s, i, a);
} else {
g.add_edge(i, t, a);
}
}
map<ii, int> mp;
REP (i, m) {
int u, v, c; cin >> u >> v >> c;
u--; v--;
if (u > v) swap(u, v);
g.add_edge(u, v, c);
}
cout << g.max_flow(s, t) << endl;
}
その他
- 競プロer、予想の100倍ぐらい起床力が高かった。
- 駅まで歩いて帰るときにけんちょんさん(@drken1215)とたくさんお話出来ました。燃やす埋めるの応用の講義をしてもらいました。
day3に続きます。