알고리즘/SW Expert Academy
[SW Expert Academy] 5648. 원자 소멸 시뮬레이션 C/C++ 풀이
Chavo Kim
2020. 8. 5. 21:12
또뮬레이션 문제
저번 문제를 나름 잘풀었다고 생각했고, 그에 비하면 방향 전환도 없는 개꿀문제라고 생각했다가 큰 코 다친 문제.
우선 이번 문제를 풀면서 느낀 것은
벡터는 정적 배열에 비해서 연산 속도가 엄청나게 느리다.
로직은 거의 똑같이 적은 두 코드를 비교하자면
#include <cstdio>
#include <vector>
#include <utility>
using namespace std;
FILE* in = stdin;
//원자의 상태를 저장하는 struct
struct mole_info {
int x;
int y;
int dir;
int e;
};
int t, n;
int dx[4] = { 0, 0, -1, 1 };
int dy[4] = { 1, -1, 0, 0 };
// 폭발해서 모인 에너지를 저장해놓음
int delay[4001][4001];
int bomb(vector<mole_info>& mole) {
int energy = 0;
//mole 벡터가 비어있지 않다면 진행
while (!mole.empty()) {
for (int i = 0; i < mole.size(); i++) {
//delay 평면에 기존 x, y 값에 에너지 값을 없애줌
delay[mole[i].x][mole[i].y] = 0;
//위치 갱신
mole[i].x += dx[mole[i].dir];
mole[i].y += dy[mole[i].dir];
//만약 범위를 벗어나지 않았다면 새로운 x,y 평면에 에너지 값 더해주기
if(mole[i].x >= 0 && mole[i].x <= 4000 && mole[i].y >= 0 && mole[i].y <= 4000)
delay[mole[i].x][mole[i].y] += mole[i].e;
}
//벡터 값 갱신을 위한 새 벡터에 기존 값을 그대로 옮겨준다.
vector<mole_info> tmp_vector;
tmp_vector.assign(mole.begin(), mole.end());
mole.clear();
for (int i = 0; i < tmp_vector.size(); i++)
{
//원자의 위치가 좌표평면 범위 안에 있고
if (tmp_vector[i].x >= 0 && tmp_vector[i].x <= 4000 && tmp_vector[i].y >= 0 && tmp_vector[i].y <= 4000)
{
// delay 평면의 값과 에너지 값이 같다면 새 벡터에 넣어줌
if (delay[tmp_vector[i].x][tmp_vector[i].y] == tmp_vector[i].e)
{
mole.push_back(tmp_vector[i]);
}
// 만약 값이 차이가 나고, 그 값이 0이 아니라면 해당 평면에 여러 에너지 값이 더해졌다는 뜻
else if(delay[tmp_vector[i].x][tmp_vector[i].y] != 0)
{
//폭발 에너지의 총합에 그 값을 더해줌
energy += delay[tmp_vector[i].x][tmp_vector[i].y];
//해당 평면은 0으로
delay[tmp_vector[i].x][tmp_vector[i].y] = 0;
}
}
}
}
return energy;
}
int main() {
fscanf(in, "%d", &t);
for (int i = 0; i < t; i++)
{
fscanf(in, "%d", &n);
vector<mole_info> mole;
for (int j = 0; j < n; j++)
{
mole_info tmp;
fscanf(in, "%d %d %d %d", &tmp.x, &tmp.y, &tmp.dir, &tmp.e);
//직선으로 만날 경우 0.5 단위에서 만날 수 있으므로 2를 곱해줌
//그리고 -1000 ~ 1000 범위 안이기 때문에 1000을 더해서 모두 양수로 바꾸어줌
tmp.x = (tmp.x * 2 + 2000);
tmp.y = (tmp.y * 2 + 2000);
mole.push_back(tmp);
}
printf("#%d %d\n",i+1 ,bomb(mole));
}
}
벡터를 이용한 해당 코드는 시간초과가 났다...
Dummy 문제에서 큐와 덱으로 재미를 봤던터라 벡터를 사용하는 연습을 하고 있었는데 시간 절약을 위해선 정적 배열이 짱인 것을 느끼고 정적 배열으로 새로 짠 코드..
#include <cstdio>
#include <vector>
#include <utility>
using namespace std;
FILE* in = stdin;
struct mole_info {
int x;
int y;
int dir;
int e;
};
int t, n;
int dx[4] = { 0, 0, -1, 1 };
int dy[4] = { 1, -1, 0, 0 };
int delay[4001][4001];
int bomb(int n, mole_info (&mole)[1001]) {
int energy = 0;
while(1==1)
{
// 없는 원소가 몇 개인지 판별하기 위한 변수
int non_exist = 0;
for (int i = 0; i < n; i++) {
//벡터와 달리 없어진 원자를 따로 없애주지 않기 때문에 에너지 값을 0으로 바꾸어서
//없어진 원소를 트래킹
if (mole[i].x < 0 || mole[i].x > 4000 || mole[i].y < 0 || mole[i].y > 4000 || mole[i].e == 0)
{
non_exist++;
continue;
}
delay[mole[i].x][mole[i].y] = 0;
mole[i].x += dx[mole[i].dir];
mole[i].y += dy[mole[i].dir];
if (mole[i].x >= 0 && mole[i].x <= 4000 && mole[i].y >= 0 && mole[i].y <= 4000)
delay[mole[i].x][mole[i].y] += mole[i].e;
}
//없어진 원소가 n개면 끝
if (non_exist == n)
break;
for (int i = 0; i < n; i++)
{
if (mole[i].x >= 0 && mole[i].x <= 4000 && mole[i].y >= 0 && mole[i].y <= 4000 && mole[i].e != 0)
{
if (delay[mole[i].x][mole[i].y] != mole[i].e)
{
energy += delay[mole[i].x][mole[i].y];
delay[mole[i].x][mole[i].y] = 0;
mole[i].e = 0;
}
}
}
}
return energy;
}
int main() {
fscanf(in, "%d", &t);
for (int i = 0; i < t; i++)
{
fscanf(in, "%d", &n);
mole_info mole[1001];
for (int j = 0; j < n; j++)
{
mole_info tmp;
fscanf(in, "%d %d %d %d", &tmp.x, &tmp.y, &tmp.dir, &tmp.e);
tmp.x = (tmp.x * 2 + 2000);
tmp.y = (tmp.y * 2 + 2000);
mole[j] = tmp;
}
printf("#%d %d\n", i + 1, bomb(n, mole));
}
return 0;
}
없어진 원소를 에너지 값 0으로 보내는 것을 제외하고는 똑같은 로직을 가지고 있다.
하지만 처리속도가 깡패..
<p.s.>
처음에는 while이 아닌 for(int time = 0; time < 4000; time++)으로 bomb 함수를 구현했는데 47개 정답으로 나왔다. 왜 그런가했더니 총 4001번의 이동이 가능함. -1000 에서 1000까지... 범위를 제대로 판단하도록 하자. 이것때문에 한 시간 날린건 안비밀....