結果
x--
あれ、目にゴミが…
なぜかEasyに時間がかかってしまった。その上System Testに落ちた。無力。
解答
Easy - BichromeBoard
盤面のパターンは2種類しかない。どちらかのパターンにマッチするならPossible, そうじゃないならImpossible
class BichromeBoard {
public:
vector<string> board;
int w,l;
char types[2][2] = {{'B', 'W'}, {'W', 'B'}};
bool ableToDrawType(int offset) {
bool res = true;
REP(i,w)REP(j,l) {
res = res && (board[i][j] != types[(i+offset)%2][j%2]);
}
return res;
}
string ableToDraw(vector<string> _board) {
board = _board;
w = board.size();
l = board[0].size();
return (ableToDrawType(0) || ableToDrawType(1)) ? "Possible" : "Impossible";
}
};
復習で間違えたパターン
- 周期境界条件にすると幅が奇数の時に最短距離で進むのと遠回りするので距離の偶奇が一致しない
Medium - FoldingPaper2
一方向に折りたたむ時、一番頑張っても幅が半分まで。目標値の2倍以上の間は半分に折り、二倍未満のときはあと一回で目標値にできる。
class FoldingPaper2 {
public:
int W;
int H;
int A;
int INF = 100001;
int foldPart(int i, int L) {
int ans = 0;
while(i * 2 < L){
ans++;
L = (L+1) / 2;
}
if(i != L)ans++;
return ans;
}
int fold(int i, int j, int _W, int _H) {
if(i > _W || j > _H) return INF;
return foldPart(i, _W) + foldPart(j, _H);
}
int solve(int _W, int _H, int _A) {
W = _W, H = _H, A = _A;
int res = INF;
int m = sqrt(A);
for(int i = 1; i <= m; i++) {
for(int j = i; j <= A; j++) {
if(i*j == A) {
res = min(res, fold(i,j,W,H));
res = min(res, fold(i,j,H,W));
}
}
}
return res == INF ? -1 : res;
}
};
復習で間違えたパターン
- 最大値としてうっかりW*H + 1なんかをとってしまうとintだとoverflowする(符号付きの32bit整数の場合最大値は2^31=2147483648、概算するなら2^31~(2^10)^3~(10^3)^3=10^9)
- 目標値のペアの大小と、初期状態のペアの大小が一致すると考えると落ちるケースがある。
Hard - NineEasy
そもそも問題文を理解するのにものすごい時間がかかったし、問題文を理解してからも大分長い間ウンウン唸った。
前提知識
9の倍数の判定法=各桁の和が9の倍数
解法
- あたりまえだけどそれぞれの桁は0~9の9通り
- それまでの桁で質問数分だけ、それぞれの質問で読まれた数字の和の9の剰余を持ちながら一桁づつ進む
- 各桁では、N個の質問それぞれに対し現在の桁が読み取られるときにはその桁の9の剰余を足す
- 最後まで進んですべての質問に正しく答えられるものは採用
メモ化することでO(M*10*9^N)
class NineEasy {
public:
int N, M;
vector<int> d;
int memo[20][9][9][9][9][9];
int solve(int digit, int *r) {
if(digit == M){
REP(i,5)if(r[i] != 0) return 0;
return 1;
}
int *m = &memo[digit][r[0]][r[1]][r[2]][r[3]][r[4]];
if(*m != -1) return *m;
int res = 0;
int nr[5];
REP(i,10) {
REP(j,5) {
nr[j] = r[j];
if(d[digit] & (1 << j)) nr[j] = (nr[j] + i) % 9;
}
res += solve(digit + 1, nr);
res %= 1000000007;
}
return *m = res;
}
int count(int _N, vector<int> _d) {
N = _N, d = _d, M = _d.size();
memset(memo, -1, sizeof(memo));
int ini[] = {0, 0, 0, 0, 0};
return solve(0, ini);
}
};
絶対もうちょっとましな書き方ある。Div1 MediumではMが大きくてもう一工夫必要みたい。
所感
- せめてEasyはとりたかった
- そもそも離散的な問題をコンピュータに解かせることに慣れていない(と思いたい)。
- DPが良く出るらしいが、全然書けない。慣れが圧倒的に足りない
- コーナーケースに対する嗅覚が全くない。従ってchallengeでの攻撃力もない。失敗をしていくうちに養われるものであると信じたい。
- 考察で問題をシンプルにする段階にもう少し時間を割いたほうがいいのかもしれない。
- プログラミングコンテストにおいて不必要な最適化があり、そういうのはしないほうが良い
- 定数倍の最適化のために、時間やコードの単純さを犠牲にしない
- 正しかったとしても定数倍しか効かないのに裏のとれていない正しくない最適化をしてしまって落ちるケースがあった。正しさが正義なので保守的に。
- 定数倍の最適化のために、時間やコードの単純さを犠牲にしない