COCI 2006/2007 contest #1 개인 풀이
1. BOJ 2052 나머지
각 나머지에 대해 marking 해놓는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<cstdio>
bool chk[42];
int main(){
for(int i=0;i<10;++i){
int t;
scanf("%d",&t);
chk[t%42] = true;
}
int ans = 0;
for(int i=0;i<42;++i)
ans += chk[i];
printf("%d",ans);
return 0;
}
2. BOJ 3053 택시 기하학
유클리드 기하에서 원 넓이는 $\pi r^2$, 택시 기하에서의 원 넓이는 $2r^2$ (한 변의 길이가 $\sqrt{2} r$인 정사각형 모양)
1
2
3
4
5
6
7
8
9
#include<stdio.h>
#define PI 3.14159265358979323846
int main()
{
double r;
scanf("%lf",&r);
printf("%lf\n%lf\n",PI*r*r,2*r*r);
return 0;
}
3. BOJ 3054 피터팬 프라임
단순 구현 문제
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
#include<cstdio>
#include<string>
using namespace std;
string p[5] = {"..#..", ".#.#.", "#...#", ".#.#.", "..#.."};
string w[5] = {"..*..", ".*.*.", "*...*", ".*.*.", "..*.."};
int main(){
char str[16] = {0};
scanf("%s",str);
string ans[5] = {".", ".", "#", ".", "."};
for(int i=0;str[i];++i){
if((i+1)%3){
for(int j=0;j<5;++j)
for(int k=1;k<5;++k)
ans[j].push_back(p[j][k]);
}else{
for(int j=0;j<5;++j){
ans[j].pop_back();
for(int k=0;k<5;++k)
ans[j].push_back(w[j][k]);
}
}
ans[2][4*i+2] = str[i];
}
for(int i=0;i<5;++i)
puts(ans[i].c_str());
return 0;
}
4. BOJ 3055 탈출
문제를 보고 BFS를 떠올릴 수 있다. 고슴도치는 다음 시간에 물이 찰 예정인 칸으로는 이동할 수 없다. 따라서 BFS과정에서 물이 먼저 움직이고 고슴도치가 움직이면서 탈출할 수 있는지를 체크한다. “먼저 움직인다”라는 것은 BFS에서 queue에 먼저 들어가는 것으로 구현해줄 수 있다.
전체 시간복잡도는 $O(RC)$
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
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int> ii;
typedef pair<ii,ii> iiii;
const int dx[] = {0,0,-1,1};
const int dy[] = {-1,1,0,0};
int r, c;
char map[50][51];
int solve(){
queue<iiii> q;
int v[50][50] = {0};
int bx, by, sx, sy;
for(int i=0;i<r;++i)
for(int j=0;j<c;++j){
if(map[i][j]=='D')
bx = i, by = j;
else if(map[i][j]=='S')
sx = i, sy = j;
else if(map[i][j]=='*')
q.push(iiii(ii(i,j),ii(0,2))), v[i][j] = 2;
}
q.push(iiii(ii(sx,sy),ii(0,1)));
v[sx][sy] = 1;
while(!q.empty()){
int x = q.front().first.first;
int y = q.front().first.second;
int t = q.front().second.first;
int w = q.front().second.second;
q.pop();
if(w==1 && x==bx && y==by)
return t;
for(int i=0;i<4;++i){
int nx = x + dx[i];
int ny = y + dy[i];
if(nx<0 || nx>=r) continue;
if(ny<0 || ny>=c) continue;
if(map[nx][ny]=='X') continue;
if(map[nx][ny]=='D' && w==2) continue;
if(v[nx][ny] >= w) continue;
v[nx][ny] = w;
q.push(iiii(ii(nx,ny),ii(t+1,w)));
}
}
return -1;
}
int main(){
scanf("%d%d\n",&r,&c);
for(int i=0;i<r;++i)
scanf("%s",map[i]);
int ans = solve();
if(ans<0) puts("KAKTUS");
else printf("%d",ans);
return 0;
}
5. BOJ 3056 007
Dynamic Programming
+ Bitmask
로 해결할 수 있다.
$0 \sim n-1$번 요원이 순서대로 미션을 할당받는다고 하자. $i-1$번 요원까지 미션을 할당받았을 때, $i$번 요원은 남은 미션들 중 하나를 수행하게 된다. 할당된 임무 목록을 $state$에 bitmasking하면 다음과 같이 정의할 수 있다.
- $dp[i][state]$ = ($0 \sim i$번 요원까지 할당된 임무 목록이 $state$일 때, 이를 모두 성공적으로 끝낼 확률)
이 때 $i$는 필요없는 정보인 것을 알 수 있는데, $state$의 켜진 bit 수와 같기 때문이다. 즉, $state$만으로 몇 명의 요원까지 미션을 할당받았는지 알 수 있다는 말이다. 다음과 같이 다시 정의하자.
- $dp[state]$ = (할당된 임무 목록이 $state$일 때, 이를 모두 성공적으로 끝낼 확률 )
이 때, 임무를 하나 더 할당했을 때 $state$가 되는 임무 목록들을 $prev$라고 하자. $state$가 포함하는 미션이 $i$개이고, $j$번 미션을 할당하는 경우, 다음과 같이 계산할 수 있다.
\[dp[state] = \underset{bit \in \forall prev}{\max}(dp[bit] \cdot p[i][j])\]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
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int n;
double p[20][20];
double dp[1<<20];
double solve(){
for(int i=0;i<n;++i)
dp[1<<i] = p[0][i];
for(int i=1;i<n;++i)
for(int bit=0;bit<(1<<n);++bit){
int cnt = 0;
for(int j=bit;j;j/=2)
cnt += j%2;
if(cnt!=i) continue;
for(int j=0;j<n;++j)
if((bit & (1<<j))==0)
dp[bit|(1<<j)] = max(dp[bit|(1<<j)], dp[bit]*p[i][j]);
}
return dp[(1<<n)-1];
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;++i)
for(int j=0;j<n;++j){
scanf("%lf",&p[i][j]);
p[i][j] /= 100.0f;
}
printf("%.9lf",solve()*100.0f);
return 0;
}
6. BOJ 3057 디버그 / BOJ 1751 디버그
모든 정방행렬에 대해 180도 회전시켜보고 같은지 확인해야 한다. $N = min(R,C)$라하면, 모든 좌표에 대해 대해 정방행렬은 최대 $N$개 존재할 수 있으므로 검사해야 할 정방행렬은 $N^3$개 존재한다. 각 정방행렬을 돌려서 비교하는데 $O(N^2)$으로 합쳐서 $O(N^5)$에 TLE를 받을 수 있다. 정방행렬을 돌려서 확인하는 과정을 최적화시켜보자.
크기가 $k$인 정방행렬이 스퀘어 킬러일 때, 이를 감싸는 테두리가 180도 돌려도 같다면 크기가 $k+2$인 정방행렬이 존재하는 것이다. 즉, 각 정방행렬에 대해 스퀘어 킬렁인지를 저장해두고 테두리만 확인해주면 $O(N)$에 정방행렬인지 판별이 가능하다.
어떤 정방행렬의 가장 아래-오른쪽 원소를 기준으로 dp식을 정의하자.
- $dp[x][y][k]$ = (가장 아래-오른쪽 원소가 $(x,y)$이고 크기가 $k$인 정방행렬이 스퀘어 킬러이면
true
)
테두리를 확인하는 함수를 다음과 같이 정의하면
- $cmp(i,j,k)$ = ($dp[x][y][k]$에 해당하는 행렬의 테두리가 180도 돌려도 같으면
true
)
점화식을 얻을 수 있다.
- $dp[x][y][k] = dp[x-1][y-1][k-2] \ \And \ cmp(i,j,k)$
이렇게 하면 $O(N^4)$에 해결할 수 있다. 여기서 더 줄일 수도 있는데, 모든 원소는 bool
이므로 64bit 자료형에 넣고 bitwise로 비교하면 $O(N^4 / 64)$에 해결할 수 있다.
참고로 3057번과 1751번의 차이는 크기가 1인 정방행렬을 스퀘어 킬러로 볼 것인가 아닌가의 차이이다.
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
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
int r, c, m[300][300];
bool dp[300][300][300];
ll row[300][300], rrow[300][300];
ll col[300][300], rcol[300][300];
bool chk(int x,int y,int size){
int si = x-size+1, ei = x;
int sj = y-size+1, ej = y;
if(si<0 || sj<0) return false;
int bit;
for(int k=0;k<size;k+=bit){
bit = min(60,size-k);
ll a = (row[si][sj+k] & ((1LL<<bit)-1));
ll b =(rrow[ei][ej-k] & ((1LL<<bit)-1));
if(a != b) return false;
ll c = (col[si+k][sj] & ((1LL<<bit)-1));
ll d =(rcol[ei-k][ej] & ((1LL<<bit)-1));
if(c != d) return false;
}
return true;
}
int solve(){
for(int i=r-1;i>=0;--i)
for(int j=c-1;j>=0;--j){
if(j==c-1) row[i][j] = m[i][j];
else row[i][j] = 2LL*row[i][j+1] + m[i][j];
if(i==r-1) col[i][j] = m[i][j];
else col[i][j] = 2LL*col[i+1][j] + m[i][j];
}
for(int i=0;i<r;++i)
for(int j=0;j<c;++j){
if(j==0) rrow[i][j] = m[i][j];
else rrow[i][j] = 2LL*rrow[i][j-1] + m[i][j];
if(i==0) rcol[i][j] = m[i][j];
else rcol[i][j] = 2LL*rcol[i-1][j] + m[i][j];
}
int ans = 1;
int upp = min(r,c);
for(int i=0;i<r;++i)
for(int j=0;j<c;++j)
dp[i][j][0] = dp[i][j][1] = true;
for(int i=1;i<r;++i)
for(int j=1;j<c;++j)
for(int k=2;k<=upp;++k)
if(dp[i-1][j-1][k-2] && chk(i,j,k)){
dp[i][j][k] = true;
ans = max(ans, k);
}
return ans>1 ? ans : -1;
}
int main(){
scanf("%d%d",&r,&c);
for(int i=0;i<r;++i)
for(int j=0;j<c;++j)
scanf("%1d",&m[i][j]);
printf("%d",solve());
}