BOJ
BOJ::17143 낚시왕
BOJ Gold 3
BOJ::17143 낚시왕
[BOJ] : https://www.acmicpc.net/problem/17143
- Level : Gold 3
시사점
- 구현 문제입니다.
- 잘잘한 실수가 모여서 태산이 됩니다.
키
- #낚시왕, #상어
이해(x)
- 맵이 주어집니다.
-
상어의 리스트가 주어집니다.
- 낚시왕이 0열부터 끝 열까지 이동합니다.
- x열에 도착할때마다 가장 가까운 행에 있는 상어를 잡아먹습니다.
- 이후, 남은 상어들은 가지고 있는 방향과 속도만큼 이동합니다.
- 한 칸에 상어가 2마리 이상 존재한다면, 제일 큰 상어를 제외한 나머지는 죽습니다.
- 총 이동하는 동안 잡힌 상어의 크기를 출력합니다.
설계(x)
- 상어의 List와 map 둘 다 사용하여 문제를 풀이합니다.
- 업데이트가 중요한 문제입니다.
업데이트가 필요한 변수
- a[][], shark
시간 복잡도
- (2 ≤ R, C ≤ 100, 0 ≤ M ≤ R×C)
- r, c, s, d, z (1 ≤ r ≤ R, 1 ≤ c ≤ C, 0 ≤ s ≤ 1000, 1 ≤ d ≤ 4, 1 ≤ z ≤ 10000)
-
d가 1인 경우는 위, 2인 경우는 아래, 3인 경우는 오른쪽, 4인 경우는 왼쪽
- naive of naive 설계시
- O(N(최대 열)) * O(s(상어의 이동) * M(마릿수))
-
O(100) * O(100 * 100 * 1000) = 10^9 -> TL
- 이 숫자들 중 뭘 줄일 수 있는가? -> s
- 행 방향 이동하는 s는 s %= (m-1)
- 열 방향 이동하는 s는 s %= (n-1) 과 같이 줄일 수 있음.
- 즉, 행 방향 이동의 경우 m번마다 같은 위치에 같은 방향을 바라보는 상태가 됨.
-
list가 나왔다 && 충돌 처리 -> map + list 로 할까 map으로 할까 list로 할까
- 문제를 풀때, 값이 바뀌는 변수들을 미리 정리하자. 이번엔 a[][]에 값을 입력하지도 않고 시작함.
- 예를 들어, 이 문제에서는 a[][]와 shark의 값이 계속 바뀐다.
- 따라서, update와 downdate를 계속 해줘야 한다.
공간 복잡도
구현(x)
#include<iostream>
#include<vector>
#include<algorithm>
const int MAX_N = 100 + 1;
const int MAX_SH = 100 * 100;
struct cell{ int x; int y; int s; int d; int z; };
const int nd[] = { 0, 2, 1, 4, 3 };
const int dx[] = { 0, -1, 1, 0, 0 }, dy[] = { 0, 0, 0, 1, -1 };
using namespace std;
int n, m, cntSh;
int a[MAX_N][MAX_N];
vector<cell> shark;
// 실수 : return (x == 1 || y == 1 || x == n || y == m); -> 방향에 위배되는 것만 넣어야하는데..
bool ontheLine(int x, int y, int d){
if (d == 1 && x == 1) return true;
if (d == 2 && x == n) return true;
if (d == 3 && y == m) return true;
if (d == 4 && y == 1) return true;
return false;
}
void moveShark(int&x, int&y, int&d, int s){
int nx = x, ny = y;
// 와.. 예전에 한 실수를 또함.. 초기 위치부터 ontheLine에 있을 수 있으므로
// 해당 if문 부터 실행 시킨 후에 -> dx, dy 더해줘야함.
while (s--){
if (ontheLine(nx, ny, d)){
d = nd[d];
}
nx += dx[d], ny += dy[d];
}
x = nx, y = ny;
}
int simulate(){
int column = -1, ret = 0;
for (column = 1; column <= m; column++){
// fishman kill the closest
for (int i = 1; i <= n; i++)
if (a[i][column] > 0){
ret += a[i][column];
// 상어 list에 사망 처리 해줘야함
a[i][column] = 0;
break;
}
// sharks move
// map a에 있는 데이터 또 안지울뻔.. 하지만 이번엔 a는 버릴 것이므로 불필요
int b[MAX_N][MAX_N] = { 0 };
// for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)b[i][j] = 0;
for (int i = 0; i < shark.size(); i++){
// dead guys
if (shark[i].z == 0 || a[shark[i].x][shark[i].y] != shark[i].z){
shark[i].z = 0;
continue;
}
int x = shark[i].x, y = shark[i].y, d = shark[i].d, s = shark[i].s, z = shark[i].z;
int nx = x, ny = y, nd = d;
moveShark(nx, ny, nd, s); // nx, ny, nd are updated
shark[i].x = nx, shark[i].y = ny, shark[i].d = nd;
b[nx][ny] = max(b[nx][ny], z);
}
for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)
a[i][j] = b[i][j];
}
return ret;
}
int main(){
freopen("input.txt", "r", stdin);
cin >> n >> m >> cntSh;
shark = vector<cell>(cntSh);
for (int i = 0; i < cntSh; i++){
cin >> shark[i].x >> shark[i].y >> shark[i].s >> shark[i].d >> shark[i].z;
// 실수 (n-1) *2, (m-1) *2로 나눈 나머지 인데 *2를 각각 빼먹음
if (shark[i].d <= 2) shark[i].s %= 2 * (n - 1);
else shark[i].s %= 2 * (m - 1);
a[shark[i].x][shark[i].y] = shark[i].z;
}
cout << simulate() << endl;
return 0;
}
2nd try ( 60m )
#include<bits/stdc++.h>
#define endl '\n'
#define X first
#define Y second
#define pb push_back
#define rep(i,a,b) for(int i=a;i<b;i++)
#define r_rep(i,a,b) for(int i=a;i>b;i--)
struct cell{int x; int y; int spd;int d; int sz;};
const int MAXNM = 100;
const int dx[]={-1, 1, 0, 0}, dy[]={0, 0, 1, -1};
using namespace std;
int n, m, cntsh;
vector<cell> sh;
int a[MAXNM][MAXNM];
void onEdge(int& x, int& y, int & d){
if(d == 0 && x == 0) d = 1;
else if(d == 1 && x == n-1) d = 0;
else if(d == 2 && y == m-1) d = 3;
else if(d == 3 && y == 0) d = 2;
}
void input(){
cin >> n >> m >> cntsh;
rep(i, 0, cntsh){
int x, y, spd, d, sz;
cin >> x >> y >> spd >> d >> sz;
d -= 1, x-=1, y-=1;
// 실수(5) : n, m 바꿔씀
if(d >= 2) spd = spd % ( (m-1) *2 ); // 실수(10) : 등호 안 넣음
else spd = spd % ( (n-1) *2 );
onEdge(x, y, d); // 실수(4) : 처음부터 벽보고 있을 수 있음, 따라서 해당 라인 추가해줌
sh.push_back({x, y, spd, d, sz});
a[x][y] = sz;
}
}
void process(){
int ans = 0;
input();
rep(fisher, 0, m){
// fisher kill
rep(i, 0, n) if(a[i][fisher] != 0){
ans += a[i][fisher];
a[i][fisher] = 0;
break; // 실수(12) : break 안함
}
int b[MAXNM][MAXNM] = {0,};
// shark move
rep(i, 0, sh.size()) if(sh[i].sz != 0){
if(a[sh[i].x][sh[i].y] != sh[i].sz){sh[i].sz = 0; continue;}
int x = sh[i].x, y = sh[i].y, spd = sh[i].spd, &d = sh[i].d, &sz = sh[i].sz;
int nx = x, ny = y;
while(spd--){
nx += dx[d], ny += dy[d];
onEdge(nx, ny, d);
}
// cmp
if(b[nx][ny] < sz){
b[nx][ny] = sz;
}else sz = 0;
// De-init ( 불필요 )
// init
sh[i].x = nx, sh[i].y = ny;
}
memcpy(a, b, sizeof(a));
}
cout << ans << endl;
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
process();
return 0;
}
디버깅(x)
- 많은 실수를 하였습니다.
- input을 받은 후 속도를 (행 혹은 열의 크기-1) * 2 로 나눈 나머지로 구해야 하지만, * 2 를 하지 않았습니다.
- map으로 사용하는 a[][]에 downdate를 하지 않았습니다.
- ontheLine 함수에서 방향에 상관없이 경계에 있기만 하면 true를 리턴했습니다.
- moveShark 함수에서 상어의 초기 위치가 경계선 위에 있는 경우를 처리하지 못했습니다.
2nd try
- 입력받아서 speed의 값을 축소시켜주는 작업을 할때, n과 m을 바꿔서 사용했습니다. (5m)
- speed의 값을 축소시키는 것은, 방향에 따라 달라집니다. ( 10m )
- 방향을 0,1과 2,3으로 나눠야 하지만 d>2 와 else로 사용했습니다.
- 처음부터 상어가 벽을 보고 있는 경우에 대한 처리를 해주지 않았습니다. ( 4m )
- process함수에서 낚시꾼이 가장 가까운 상어를 잡으면 break해주어야 하지만 처리해주지 않았습니다. (12m)