SRM 638 Div1 Easy, ShadowSculpture
問題
http://community.topcoder.com/stat?c=problem_statement&pm=13355&rd=16081
N*N*N (N <= 10) の空間に 1*1*1 のキューブをいくつか配置する. XY・YZ・ZX平面の投影図がそれぞれ与えられる. 配置したキューブは全て隣接し得るかどうか答えよ.
方針
座標 (i, j, k) にキューブが存在し得る条件: XY[i][j] && YZ[j][k] && ZX[k][i]
- 各座標について,
- DFSで存在し得る隣接キューブにマークを付ける
- マークがついたキューブから 3 平面の投影図を作り, 入力と一致するかどうかチェックする
解答
#include <iostream> #include <vector> #include <algorithm> #define loop(n, i) for(int i=0;i<n;i++) #define from_to(from, to, i) for(int i=from;i<=to;i++) using namespace std; class ShadowSculpture { public: string possible (vector <string> XY, vector <string> YZ, vector <string> ZX) { this->XY = XY; this->YZ = YZ; this->ZX = ZX; N = XY.size(); loop (N, i) loop (N, j) loop (N, k) { if (XY[i][j] == 'Y' && YZ[j][k] == 'Y' && ZX[k][i] == 'Y') { A[i][j][k] = 1; } } loop (N, i) loop (N, j) loop (N, k) used[i][j][k] = 0; loop (N, i) loop (N, j) loop (N, k) { loop (N, i) loop (N, j) loop (N, k) visited[i][j][k] = 0; dfs(i, j, k); if (check()) return "Possible"; } return "Impossible"; } vector<string> XY, YZ, ZX; int N; int A[10][10][10] = {}; int used[10][10][10]; int visited[10][10][10]; void dfs(int i, int j, int k) { if (used[i][j][k] || !A[i][j][k]) return; used[i][j][k] = visited[i][j][k] = 1; if (i-1 >= 0) dfs(i-1, j, k); if (i+1 < N) dfs(i+1, j, k); if (j-1 >= 0) dfs(i, j-1, k); if (j+1 < N) dfs(i, j+1, k); if (k-1 >= 0) dfs(i, j, k-1); if (k+1 < N) dfs(i, j, k+1); } bool check() { int xy[10][10] = {}, yz[10][10] = {}, zx[10][10] = {}; loop (N, i) loop (N, j) loop (N, k) { if (visited[i][j][k]) { xy[i][j] = yz[j][k] = zx[k][i] = 1; } } loop (N, i) loop (N, j) { if (XY[i][j] == 'Y' && !xy[i][j]) return false; if (YZ[i][j] == 'Y' && !yz[i][j]) return false; if (ZX[i][j] == 'Y' && !zx[i][j]) return false; } return true; } };
SRM 633 Div1 Easy, PeriodicJumping
問題
http://community.topcoder.com/stat?c=problem_statement&pm=13234&rd=16076
int x, vector<int> jumpLengths が与えられる.
二次元平面上の原点 (0, 0) からスタートして, 点 (x, 0) に到達したい.
平面上を jumpLengths で与えられた距離だけ順に移動する. (jumpLengths = {2, 5} の場合は, 距離 2, 5, 2, 5, .... ずつ移動する. )
最短何回の移動で到達できるか.
- -109 <= x <= 109
- jumpLength contains 1 between 50 elements.
- a ∈ jumpLength について, -109 <= a <= 109
方針
多角形の成立条件: 最大辺の長さ <= その他の辺の合計長
を使う.
n 回の移動で辺 (0, 0) to (x, 0) を含む多角形を構成できるかどうかを確かめる.
N = len(jumpLength), S = sum(jumpLength) とする.
- x == 0 の場合
- 解は 0
- x > S の場合
- 最大辺の長さは x
- 移動距離の合計が初めて x を超えた時の移動回数を求めれば良い
- x <= S の場合
- 最大辺の長さは max(x, max(jumpLength))
- 少なくとも 2N 回の移動で多角形を構成できる.
- n = 1, ..., 2N について, n 回の移動で多角形を構成できるかどうか確かめれば良い.
解答
class PeriodicJumping { public: int minimalTime(int x, vector <int> jumpLengths) { x = abs(x); int N = jumpLengths.size(); ll S = 0; for (int l : jumpLengths) S += l; if (x == 0) return 0; if (x <= S) { loop (N*2, i) { ll m = 0, len = 0; loop (i+1, j) m = max(m, (ll)jumpLengths[j%N]); m = max(m, (ll)x); loop (i+1, j) len += jumpLengths[j%N]; len += x; if (m <= len/2) return i + 1; } } if (x > S) { int ans = N * (x/S); x %= S; for (int l : jumpLengths) { if (x <= 0) break; ans++; x -= l; } return ans; } return -1; // dummy } };
感想
本番では, 愚直に移動回数毎の移動可能範囲を求めて解いた. 2N 回の移動で半径 2S の範囲をカバーできることに気付くまでに時間がかかった. 凡ミスで failed systest.
まともな幾何の問題は初めてだった. 素振りが足りない.
SRM 632 Div1 Easy, PotentialArithmeticSequence
問題
a[0], ..., a[N-1] の N 個の非負整数がある. ただし, a は与えられない. 代わりに, 2進数表現での trailing zero の数 d[0], ..., d[N-1] が与えられる. a[0], ..., a[N-1] について, 1 ずつ増加する部分列の個数の最大値を求めよ.
- 1 <= N <= 50
- 0 <= d[i] <= 109
解法
全ての i, j (i < j) について d[i], ..., d[j] が 1 ずつ増加する数列になりうるかどうかを確かめる.
必要十分条件: 2k+1 個おきに, k が出現する
解答
class PotentialArithmeticSequence { public: int numberOfSubsequences (vector <int> d) { int N = d.size(); int ans = 0; loop (N, i) loop (N, j) { if (i > j) continue; vector<int> e; from_to(i, j, k) e.push_back(d[k]); bool ok = true; int k = 0; for (; ok && e.size() > 1; k++) { int l = e[e.size()-1] == k ? e.size()-1 : e.size()-2; while (ok && l >= 0) { if (e[l] == k) e.erase(e.begin()+l); else ok = false; l -= 2; } } if (ok && e[0] >= k) ans++; } return ans; } };
SRM 630 Div1 Med, SuffixArrayDiv1
問題
http://community.topcoder.com/stat?c=problem_statement&pm=13377&rd=16061
ある文字列に対する接尾辞配列 SA が与えられている. 元の文字列は何種類のアルファベットで構成されているか. 最も少ない場合の種類数を求めよ.
接尾辞配列 - Wikipedia
文字列長は 1 以上 50 以下とする.
解法
接尾辞を辞書順に並べた時に, 先頭文字が最低何回変わらなければならないかを考える.
文字列の i 文字目を c(i), i 文字目以降の接尾辞を suff(i) で表すと,
suff(i) < suff(j) c(i) = c(j) かつ suff(i+1) < suff(j+1), または c(i) < c(j)
が成り立つ. これを用いて, 各 i について suff( SA[i] ) と suff( SA[i+1] ) の先頭文字を等しく出来るかどうかを確かめる.
解答
#include <iostream> #include <vector> #include <algorithm> #define loop(n, i) for(int i=0;i<n;i++) #define from_to(from, to, i) for(int i=from;i<=to;i++) using namespace std; class SuffixArrayDiv1 { public: int minimalCharacters (vector <int> SA) { int N = SA.size(); vector<int> I(N+1); loop (N, i) I[SA[i]] = i; I[N] = -1; int cnt = 0; loop (N-1, i) { if (I[SA[i]+1] > I[SA[i+1]+1]) cnt++; } return cnt+1; } };
SRM 630 Div1 Easy, Egalitarianism3
問題
http://community.topcoder.com/stat?c=problem_statement&pm=13284&rd=16061
頂点数 n, 辺数 n-1 の木が与えられる. 各辺には距離が設定されている. k 個の頂点を選び, 頂点間の距離が全て等しくなるようにしたい. k の最大値を求めよ.
頂点数は 1 以上 50 以下, 各辺の距離は 1 以上 1000 以下とする.
解法
条件を満たす頂点集合は次の三通り
- 任意の 1 頂点 (k = 1)
- 任意の 2 頂点 (k = 2)
- スター型 (k > 2)
ハブ頂点までの距離が等しく, ハブ頂点までの路を共有しない頂点の集合
スター型については, ハブ頂点を決め, 各頂点への距離を dfs で求める.
解答
#include <iostream> #include <vector> #include <algorithm> #define loop(n, i) for(int i=0;i<n;i++) #define from_to(from, to, i) for(int i=from;i<=to;i++) using namespace std; using P = pair<int, int>; const int MAX_D = 60000; class Egalitarianism3 { public: int D[MAX_D]; int latest[MAX_D]; vector<P> G[100]; int maxCities (int n, vector <int> a, vector <int> b, vector <int> len) { loop (n-1, i) { G[a[i]-1].emplace_back(b[i]-1, len[i]); G[b[i]-1].emplace_back(a[i]-1, len[i]); } int ans = n >= 2 ? 2 : 1; loop (n, i) { fill(D, D+MAX_D, 0); fill(latest, latest+MAX_D, -1); for (P& p : G[i]) { dfs(p.first, p.second, i, p.first); } loop (MAX_D, j) ans = max(D[j], ans); } return ans; } void dfs (int n, int d, int rev, int orig) { if (latest[d] != orig) { latest[d] = orig; D[d]++; } for (P& p : G[n]) if (p.first != rev) { dfs(p.first, d+p.second, n, orig); } } };
備考
任意の 2 頂点のパターンを見逃してる人が多かった.
SRM 628 Div1 Easy, DivisorsPower
問題
http://community.topcoder.com/stat?c=problem_statement&pm=13241&rd=16009
は x の 1 以上 x 以下の約数の個数を表す. とする. 与えられた整数 について, を満たす最小の x を求めよ. 存在しない場合は -1 を返す.
解法
より, の値は最大でも60である.
について を求め, の関係が条件を満たしているかどうか確かめれば良い.
解答
class DivisorsPower { public: long long findArgument (long long n) { for (int i = 60; i >= 2; i--) { ll x = pow(1.0*n, 1.0/i) + EPS; ll p = 1; repeat (i) p *= x; if (p == n && divisors(x) == i) return x; } return -1; } ll divisors(int n) { int cnt = 0; for (int i = 1; i*i <= n; i++) { if (n%i) continue; cnt++; if (i != n/i) cnt++; } return cnt; } };
備考
本番では について を求めてハッシュマップに保存, の場合だけ別処理, みたいなめんどくさいことをしてしまった.
75分まるまる使って AC.
SRM 627 Div1 Easy, HappyLetterDiv1
問題
http://community.topcoder.com/stat?c=problem_statement&pm=13274
[a-z] のみからなる文字列 L (1 <= |L| <= 50) が与えられる. 一度の操作で, 文字列中の任意の異なる文字ペアを消去する (例えば "aabbc" -> "aab"). この操作を繰り返して, 最後に残ったアルファベットを winning letter と呼ぶ. winning letter になりうるアルファベットを全て求めよ.
解法
あるアルファベット c について, c が winning letter になりうる条件は次の通り.
c 以外の文字に対して操作を繰り返して, 「c の個数 > c 以外の文字の個数」にすることができる
c 以外の文字をうまく減らす戦略を考える. c を除いて最も個数が多い 2 文字を消していく戦略が考えられる. これを実装する.
最適性の証明が出来なくて苦しかったけど, AC だった.
解答
class HappyLetterDiv1 { public: string getHappyLetters (string letters) { int chars[26] = {}; for (char c : letters) chars[c-'a']++; string ans = ""; loop (26, i) { int cs[26] = {}; loop (26, j) if (j != i) cs[j] = chars[j]; while (1) { sort(cs, cs+26); if (cs[24] == 0) { if (cs[25] < chars[i]) ans += i + 'a'; break; } cs[24]--; cs[25]--; } } return ans; } };