일기형식으로 글을 써보겠습니다.
시간은 일요일 오후 6시.
아침부터 알고리즘 강의를 10~1, 2~5시를 하고나서 저녁도 못먹고 참가하는 대회이고, 졸려 죽겠어서 참가할지 말지 고민을 엄청많이 했지만, 일단 참가를 하기로 결정했다.
배고파죽겠다.
대회는 6시이고 지금 시간은 5:50 일단 밥은 6시부터 먹을 수 있을듯하니, 밥을 먹으면서 문제를 풀면서 대회를 참가해야 겠다.
6시 대회가 시작되었다.
A번 문제를 열었다.
첫 문장을 보니 해리포터랑 볼드모트가 무슨 마법을 서로를 향해서 쏜다는 문제인 것 같은데, 뭔 소린지 읽기가 귀찮아서 힌트를 봤다.
속도가 같으면, 복도 중간에서 만난다는 얘기가 써있었다. 아 이문제는 서로 마법을 쏘는데, 그 마법이 만나는 지점을 구하는 문제라는 것을 알았다.
예제를 보니, 첫 번째 예제 100 50 50은 정답이 50이라 중간인게 분명하고, 두 번째 예제 199 60 40은 119.4라는 절반 100보다 애매하게 큰 수가 나온걸 봐서 6:4 내분하는 점인 것 같다.
일단 코드를 짜보고 틀리면 다시 문제를 읽기로 결정하고 코드를 작성했다.
#include <cstdio>
using namespace std;
int main() {
double l,p,q;
scanf("%lf%lf%lf",&l,&p,&q);
printf("%.10lf\n",l*p/(p+q));
return 0;
}
맞았다!
이제 B를 읽어보자.
B는 뭔가 사람을 고용해서 뭘 바꾸고 뭘 바꾼다는거 같은데, 내용이 길어서 문제 이해를 못하겠으니 예제를 보기로 결정했다.
매우 상세한 예제 설명이 적혀있다. 설명과 예제를 보니 아! 단어에 적힌 알파벳을 입력으로 주어진 쌍 M개로 알파벳을 서로 교환하는 문제라는 것을 알았다.
일단 코드를 작성했다.
#include <cstdio>
#include <algorithm>
using namespace std;
char w[222];
char s[222222];
char ww[222];
int main() {
int n,m;
scanf("%d %d\n",&n,&m);
scanf("%s\n",s);
for (int i='a'; i<='z'; i++) {
w[i] = i;
}
for (int i=0; i<m; i++) {
char x, y;
scanf("%c %c\n",&x,&y);
swap(w[x],w[y]);
}
for (int i=0; i<n; i++) {
printf("%c",w[s[i]]);
}
puts("");
return 0;
}
정답이 나오질 않는다 ㅠㅠ 종이에 직접 w배열에 어떤 값이 들어가는지 적어보니 구현을 잘못했다. 아래와 같이 수정해서 맞았다.
#include <cstdio>
#include <algorithm>
using namespace std;
char w[222];
char s[222222];
char ww[222];
int main() {
int n,m;
scanf("%d %d\n",&n,&m);
scanf("%s\n",s);
for (int i='a'; i<='z'; i++) {
w[i] = i;
}
for (int i=0; i<m; i++) {
char x, y;
scanf("%c %c\n",&x,&y);
int t1,t2;
for (int k='a'; k<='z'; k++) {
if (w[k] == x) {
t1 = k;
}
if (w[k] == y) {
t2 = k;
}
}
swap(w[t1],w[t2]);
}
for (int i=0; i<n; i++) {
printf("%c",w[s[i]]);
}
puts("");
return 0;
}
C번을 읽어보니 어떤 배열을 주어진 규칙에 따라서 새로운 배열 B로 만드는 문제였다.
- B[0] = A[0]
- B[N-1] = A[N-1]
- B[i] = {A[i-1], A[i], A[i+1]}중 많은 값
새로운 배열 B를 만들고, 그 B를 A로 결정하고, A와 B가 같아질 때까지 배열 변경을 계속해서 진행한다.
N이 500,000인데, 한 번 바꾸는걸 처리하는데 총 N번이 걸리고 답의 최대값은 아마도 N정도가 걸릴 것이기 때문에, N^2으로는 시간 초과를 받게 된다.
일단, N이 작은 경우에 대해서 모든 답을 구해보고 규칙을 찾기로 결정했다.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int f(int x, int y, int z) {
vector<int> a = {x,y,z};
sort(a.begin(),a.end());
return a[1];
}
void check(int n, int kk) {
vector<int> a;
int k = kk;
for (int i=0; i<n; i++) {
a.push_back(k%2);
k/=2;
}
reverse(a.begin(),a.end());
for (int i=0; i<n; i++) {
printf("%d",a[i]);
}
printf(" -> ");
int step = 0;
while (true) {
vector<int> b(n);
b[0] = a[0];
b[n-1] = a[n-1];
for (int i=1; i<n-1; i++) {
b[i] = f(a[i-1],a[i],a[i+1]);
}
bool ch = true;
for (int i=0; i<n; i++) {
if (a[i] != b[i]) {
ch = false;
}
}
if (ch) break;
a = b;
step += 1;
for (int i=0; i<n; i++) {
printf("%d",a[i]);
}
printf(" -> ");
}
printf("%d\n",step);
}
int main() {
int n;
cin >> n;
for (int k=0; k<(1<<n); k++) {
check(n,k);
}
return 0;
}
위의 소스는 입력으로 받은 모든 경우에 대해서 답을 구해보기로 했다.
N=3 인 경우의 답을 구해보았다.
답이 바뀌는 경우는 2가지 경우 밖에 없었다.
010 -> 000 -> 1
101 -> 111 -> 1
이제 N = 4일때 답을 구해보았다.
0000 -> 0
0001 -> 0
0010 -> 0000 -> 1
0011 -> 0
0100 -> 0000 -> 1
0101 -> 0011 -> 1
0110 -> 0
0111 -> 0
1000 -> 0
1001 -> 0
1010 -> 1100 -> 1
1011 -> 1111 -> 1
1100 -> 0
1101 -> 1111 -> 1
1110 -> 0
1111 -> 0
쳐다보니 0과 1이 반복해서 3번 이상 나타나면 변화가 시작되는 것 같다.
N = 5일때 답을 구해보았다.
00000 -> 0
00001 -> 0
00010 -> 00000 -> 1
00011 -> 0
00100 -> 00000 -> 1
00101 -> 00011 -> 1
00110 -> 0
00111 -> 0
01000 -> 00000 -> 1
01001 -> 00001 -> 1
01010 -> 00100 -> 00000 -> 2
01011 -> 00111 -> 1
01100 -> 0
01101 -> 01111 -> 1
01110 -> 0
01111 -> 0
10000 -> 0
10001 -> 0
10010 -> 10000 -> 1
10011 -> 0
10100 -> 11000 -> 1
10101 -> 11011 -> 11111 -> 2
10110 -> 11110 -> 1
10111 -> 11111 -> 1
11000 -> 0
11001 -> 0
11010 -> 11100 -> 1
11011 -> 11111 -> 1
11100 -> 0
11101 -> 11111 -> 1
11110 -> 0
11111 -> 0
놀라운 것을 찾아냈다. 01010
과 10101
을 제외한 나머지 정답은 모두 0 또는 1이다. 그런데, 답이 1번만에 바뀌는 경우는 010
, 101
, 0101
, 1010
가 등장하는 것이었고, 01010
은 2번만에 바뀌는 것이었다.
여기서 101
-> 111
, 010
->000
, 1010
-> 1100
, 0101
-> 0011
로 변한다는 것을 알았다.
길이가 10101
처럼 늘어나면, 101
->111
과 같이 11111
로 변하지만, 횟수가 2번이 된다는 것도 알았다.
변경이 되려면, 길이가 3이상이면서 1
과 0
이 번갈아 나오는 부분 문자열을 찾아야 하고, 그 길이가 홀수와 짝수에 따라서 바뀌는 방법이 바뀐다.
101
,10101
과 같이1
로 시작하는 홀수 길이 문자열은 모두1
로 변하게 되고, 변경되는 횟수는 길이/2번이다.010
,01010
과 같이0
로 시작하는 홀수 길이 문자열은 모두0
로 변하게 되고, 변경되는 횟수는 길이/2번이다.1010
,101010
과 같이1
로 시작하는 짝수길이 문자열은 앞의 절반은1
로, 뒤의 절반은0
으로 변경되며, 변경되는 횟수는 길이/2-1번이다.0101
,010101
과 같이0
로 시작하는 짝수길이 문자열은 앞의 절반은0
로, 뒤의 절반은1
으로 변경되며, 변경되는 횟수는 길이/2-1번이다.
이제 N = 10인 경우를 일부만 프린트해보고 위의 방법이 맞는지 검사해보았다.
1110010100 -> 1110001000 -> 1110000000 -> 2
1110010101 -> 1110001011 -> 1110000111 -> 2
1110010110 -> 1110001110 -> 1
1110010111 -> 1110001111 -> 1
1110011000 -> 0
1110011001 -> 0
1110011010 -> 1110011100 -> 1
1110011011 -> 1110011111 -> 1
1110011100 -> 0
1110011101 -> 1110011111 -> 1
1110011110 -> 0
1110011111 -> 0
1110100000 -> 1111000000 -> 1
1110100001 -> 1111000001 -> 1
맞다!
구현을 해서 제출을 해보자.
#include <cstdio>
using namespace std;
int a[555555];
int b[555555];
int n;
int main() {
scanf("%d",&n);
for (int i=0; i<n; i++) {
scanf("%d",&a[i]);
b[i]=a[i];
}
int l = 0;
int ans = 0;
while (l+2 < n) {
if (a[l] == a[l+2] && a[l] != a[l+1]) {
int r = l+2;
while (r+1 < n) {
if (a[r] != a[r+1]) {
r += 1;
} else {
break;
}
}
int len = r-l+1;
if (len%2==0) {
int t1 = l;
int t2 = r;
while (t1 < t2) {
b[t1] = a[l];
b[t2] = a[r];
t1++;
t2--;
}
} else {
for (int k=l; k<=r; k++) {
b[k] = a[l];
}
}
int cnt = (len-1)/2;
if (ans < cnt) ans = cnt;
l=r+1;
} else {
l+=1;
}
}
printf("%d\n",ans);
for (int i=0; i<n; i++) {
printf("%d ",b[i]);
}
puts("");
return 0;
}
와우 맞았다. 대회가 끝나고 나서 알게된 사실이지만, 문제에 불가능한 경우는 -1을 출력하라는 말이 있었다. 문제를 문제와 입력,출력 예제만 보고 풀었기 때문에, (입력 형식, 출력 형식 설명을 안 읽었다) -1을 출력한다는 조건을 보지 못해서 불가능한 경우가 있는지에 대한 생각을 해본적이 없다. 물론, 불가능한 경우는 없다.
이제 D를 풀어보자.
문제에 소수점이 많고, 수식이 필요할 것 같아서 나중에 풀기로 결정하고 E를 열었다.
1
, 2
, 3
, .
, #
으로 이루어진 N*M 크기의 배열이 있을 때, 1
, 2
, 3
을 모두 연결하는데 필요한 다리의 최소 길이를 구하는 문제다. .
만 다리로 바꿀 수 있고, #
은 바꿀 수 없다.
각 칸과 1
, 2
, 3
과의 거리를 알아야 하는데, 그걸 하기 위해서, 1
, 2
, 3
을 기준으로 모든 칸에 대해서 BFS를 돌려서 거리를 구했다.
d[k][i][j]
= (i
, j
)과 숫자 k
사이의 최단 거리
그럼 이제 모든 칸에 대해서, 1
, 2
, 3
과의 거리를 구한 다음에, 2를 빼준 것이 그 칸에서 모이는 다리를 연결하는 비용이다.
코드를 작성하고 제출을 했다.
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <numeric>
#include <list>
#include <deque>
#include <string>
#include <sstream>
using namespace std;
char a[1111][1111];
int d[3][1111][1111];
bool c[1111][1111];
int n,m;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
int arr[4][4];
int main() {
scanf("%d %d",&n,&m);
for (int i=0; i<n; i++) {
scanf("%s",a[i]);
}
for (int w=1; w<=3; w++) {
queue<pair<int,int>> q;
memset(c,false,sizeof(c));
for (int l1=0; l1<n; l1++) {
for (int l2=0; l2<m; l2++) {
if (a[l1][l2] == w+'0') {
q.push(make_pair(l1,l2));
c[l1][l2] = true;
}
}
}
while (!q.empty()) {
auto p = q.front();
q.pop();
int x = p.first;
int y = p.second;
for (int k=0; k<4; k++) {
int nx = x+dx[k];
int ny = y+dy[k];
if (0 <= nx && nx < n && 0 <= ny && ny < m) {
if (c[nx][ny] == false && a[nx][ny] != '#') {
d[w-1][nx][ny] = d[w-1][x][y]+1;
c[nx][ny] = true;
q.push(make_pair(nx,ny));
}
}
}
}
}
int ans = 0;
for (int l1=1; l1<=2; l1++) {
for (int l2=l1+1; l2<=3; l2++) {
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
if (d[l1-1][i][j] != 0 && a[i][j] == l2+'0') {
if (arr[l1][l2] == 0) {
arr[l1][l2] = d[l1-1][i][j];
} else if (arr[l1][l2] > d[l1-1][i][j]) {
arr[l1][l2] = d[l1-1][i][j];
}
}
}
}
//printf("%d %d %d\n",l1,l2,arr[l1][l2]);
if (arr[l1][l2] == 0) {
printf("-1\n");
return 0;
}
}
}
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
if (a[i][j] != '.') {
continue;
}
bool ok = true;
int cur = 0;
for (int k=0; k<3; k++) {
if (d[k][i][j] == 0) {
ok = false;
}
cur += d[k][i][j];
}
cur -= 2;
if (ok && ans > cur) {
ans = cur;
}
}
}
printf("%d\n",ans);
return 0;
}
맞았다!
채점을 받으니 틀렸다.
문제를 대충읽어서 틀리고 말았다. 인접한 숫자끼리는 다리 없이 지나갈 수 있었다.
인접한 숫자끼리 다리 없이 지나갈 수 있는 것을 처리한 소스는 아래와 같고, 아래 소스는 맞은 소스이다.
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <numeric>
#include <list>
#include <deque>
#include <string>
#include <sstream>
using namespace std;
char a[1111][1111];
int d[3][1111][1111];
bool c[1111][1111];
int n,m;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
int arr[4][4];
int main() {
scanf("%d %d",&n,&m);
for (int i=0; i<n; i++) {
scanf("%s",a[i]);
}
for (int w=1; w<=3; w++) {
queue<pair<int,int>> q;
memset(c,false,sizeof(c));
for (int l1=0; l1<n; l1++) {
for (int l2=0; l2<m; l2++) {
if (a[l1][l2] == w+'0') {
q.push(make_pair(l1,l2));
c[l1][l2] = true;
}
}
}
while (!q.empty()) {
auto p = q.front();
q.pop();
int x = p.first;
int y = p.second;
for (int k=0; k<4; k++) {
int nx = x+dx[k];
int ny = y+dy[k];
if (0 <= nx && nx < n && 0 <= ny && ny < m) {
if (c[nx][ny] == false && a[nx][ny] != '#') {
d[w-1][nx][ny] = d[w-1][x][y]+1;
c[nx][ny] = true;
q.push(make_pair(nx,ny));
}
}
}
}
}
int ans = -1;
for (int l1=1; l1<=2; l1++) {
for (int l2=l1+1; l2<=3; l2++) {
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
if (d[l1-1][i][j] != 0 && a[i][j] == l2+'0') {
if (arr[l1][l2] == 0) {
arr[l1][l2] = d[l1-1][i][j];
} else if (arr[l1][l2] > d[l1-1][i][j]) {
arr[l1][l2] = d[l1-1][i][j];
}
}
}
}
}
}
if (arr[1][2] != 0 && arr[1][3] != 0) {
if (ans == -1 || ans > arr[1][2] + arr[1][3] - 2) {
ans = arr[1][2] + arr[1][3] - 2;
}
}
if (arr[1][2] != 0 && arr[2][3] != 0) {
if (ans == -1 || ans > arr[1][2] + arr[2][3] - 2) {
ans = arr[1][2] + arr[2][3] - 2;
}
}
if (arr[1][3] != 0 && arr[2][3] != 0) {
if (ans == -1 || ans > arr[1][3] + arr[2][3] - 2) {
ans = arr[1][3] + arr[2][3] - 2;
}
}
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
if (a[i][j] != '.') {
continue;
}
bool ok = true;
int cur = 0;
for (int k=0; k<3; k++) {
if (d[k][i][j] == 0) {
ok = false;
}
cur += d[k][i][j];
}
cur -= 2;
if (ok && (ans == -1 || ans > cur)) {
ans = cur;
}
}
}
printf("%d\n",ans);
return 0;
}
댓글 (4개) 댓글 쓰기
hihihi 8년 전
댓글을 쓸 수가 있네요 신기하다
Hibbah 8년 전
그러게요.. 싱기하다
hihihi 8년 전
오오 오랜만이에요
Hibbah 8년 전
잘 지내고 계시죠 ?