abc165_cの解き方を解説 深さ優先探索(dfs)での数列作成方法
2020/05/19 14:05
感謝の正拳突き1万回ならぬ、競プロサイトで 1 日 1AC(正解)するように頑張る。
今日は飲み会のあとに挑戦したら撃沈したAtCoder Beginner Contest 165 の C 問題。
問題文の意味が難解で、プログラミングの本筋より読解能力を試される問題。 酔ってる時に読んだら意味がわからなかったけど、今読んだらわかった。
まず数列 A がある
入力例 1 の N = 3, M = 4 の場合を考えると、A のパターンは次の通り。
1 1 1
1 1 2
1 1 3
1 1 4
1 2 2
1 2 3
1 2 4
1 3 3
1 3 4
1 4 4
2 2 2
2 2 3
2 2 4
2 3 3
2 3 4
2 4 4
3 3 3
3 3 4
3 4 4
4 4 4
こんな感じの数列を全探索する。
A[b] - A[a] == c となる d の合計
上の A の数列ができたら、Q の数だけ入力が渡されるので、A[b] - A[a] == c となる行を探して d を足す。 回答の A = 1 3 4 の場合だとしたら
1 個目の a:1 b:3 c:3 d:100
A[b]は A の 3 番目の数 = 4 A[a]は A の 1 番目の数 = 1 4 - 1 = 3 となり、c が 3 なので当てはまる。
d を足す
こうやって選択された行の d を集計していくと答えが出る。
アルゴリズム
A の数列を作るのは深さ優先探索(dfs)を使う。 慣れないと読みづらいけど、こういう書き方は典型的な dfs の使い方なので、脳内で動かして練習したいところ。
A[b] - A[a] == c となる d の合計は言葉の意味は理解しづらいけど、理解しちゃえば簡単なプログラムになる。
この問題は制約でループの数が必ず少なくなるので、単純に全探索で AC となった。
// abc165_c
#include <bits/stdc++.h>
using namespace std;
int A = 0, B = 1, C = 2, D = 3;
int n,m,q,a,b,c,d;
int result;
// abcdを保存するリスト
vector<vector<int>> abcds;
// Aの数列を作るリスト
vector<int> vec_a(20);
// A[b] - A[a] = cを満たすA[d]の合計を満たす
void resolve() {
int tmp = 0;
for(auto&& abcd : abcds) {
if(vec_a[abcd[B]] - vec_a[abcd[A]] == abcd[C]) tmp += abcd[D];
}
if(result < tmp) result = tmp;
return;
}
// Aの数列を作る
void dfs(int depth) {
if(depth == n) {
// 数列が完成したら、答えを出す
resolve();
return;
}
for(int i = vec_a[depth]; i <= m; i++) {
vec_a[depth + 1] = i;
dfs(depth + 1);
}
return;
}
int main(){
cin >> n >> m >> q;
abcds.assign(q, vector<int>(4, 0));
for(int i = 0; i < q; i++) {
cin >> a >> b >> c >> d;
abcds[i][A] = a;
abcds[i][B] = b;
abcds[i][C] = c;
abcds[i][D] = d;
}
vec_a[0] = 1;
dfs(0);
cout << fixed << result;
}