[アルゴリズム解析]BOJ 20056法師サメと火球
今日の質問はBOJ 20056魔法使いサメと火球です.これはシミュレーションの問題で、私は難しいと思います.私はシミュレーションに弱いようです.
サメは魔法使いになり、火球を覚えた.
ガイドサメの大きさはN×N人グリッドはM個の火の玉を発射した.最初は、火の玉はそれぞれの位置で待機して移動します.i番火の玉の位置は(ri,ci)、質量はmi、方向はdi、速度はsiです.位置(r,c)は、r行c列を表す.
グリッドの行と列は1からN番号、1行はN番号、1列はN番号に関連付けられます.
火の玉の方向は、あるセルに隣接する8つのセルの方向を意味し、整数は次のとおりです.
魔法使いのサメがすべての火球を移動させると、次のようなことが起こります.すべての火球が自分の方向diに移動する速度はsi格である. の移動では、同じ車両に複数の火の玉がある可能性があります. 移動がすべて終了すると、火の玉が2つ以上ある格子で次のようなことが起こります. 同じ格子の火球が一つになっています. 火球は4つの火球に分かれている. に分けられた火の玉の質量、速度、方向は以下の通りである.
質量は火球質量の和である. 24172速度は、火の玉の速度の和/火の玉の数の和である. が合わさった火の玉の方向が奇数または偶数の場合、方向は0、2、4、6であり、そうでなければ1、3、5、7である. 質量がゼロの火の玉が消えます. ガイドサメはK回移動を命令した後,残りの火球質量の和を求める.
[入力]
1行目はN MK
2行目から、M行ごとにFire Ballの情報が与えられます.火球の情報は5つの整数ri,ci,mi,si,diからなる.
2つの異なる火球の位置が同じである場合、入力は与えられません.
[出力]
法師サメはK回移動を命じた後、残りの火球質量の和を出力する.
[制限] 4 ≤ N ≤ 50 0 ≤ M ≤ N2 1 ≤ K ≤ 1,000 1 ≤ ri, ci ≤ N 1 ≤ mi ≤ 1,000 1 ≤ si ≤ 1,000 0 ≤ di ≤ 7
私は問題を見ると容易ではないと思います.
特殊なアルゴリズムを用いた問題に比べて,このように複数の要素の問題を注意深く確認し,正しく処理することはより困難であるようである.
まず、問題は大きく二つの段階に分けられる.
移動火球+マージ火球再分割
また、この問題の特徴は火球に多くの要素があることです.火球の位置を表す座標と質量、速度、移動方向を覚えておいてください.私の場合、質量、速度、移動方向がpropertyの構造体
このような資料構造を宣言する方法が有効かどうか疑問だが、入力範囲が大きくないため、簡単で正確な解答方式を選んだ.
回転二次元配列
新しい座標を求める過程で苦労したが,移動速度がNより大きい場合,mod演算によりその値を小さくし,移動方向が境界を超え,移動後の座標値が0より小さいかN−1より大きい場合,処理の部分が重要である.
移動した球の二次元配列
このとき、座標中のボールが1つしかない場合は、合わせて分けて再反映した
位置上のボールの質量と速度とを求め、移動方向が一致しているかどうかを確認した後、新しい質量、速度、移動方向を反映して
このプロセスはK回行われ、最後の実行であれば、最終的に
ループの座標部分の練習が必要みたい…!
BOJ 20056魔法使いサメと火球
サメは魔法使いになり、火球を覚えた.
ガイドサメの大きさはN×N人グリッドはM個の火の玉を発射した.最初は、火の玉はそれぞれの位置で待機して移動します.i番火の玉の位置は(ri,ci)、質量はmi、方向はdi、速度はsiです.位置(r,c)は、r行c列を表す.
グリッドの行と列は1からN番号、1行はN番号、1列はN番号に関連付けられます.
火の玉の方向は、あるセルに隣接する8つのセルの方向を意味し、整数は次のとおりです.
魔法使いのサメがすべての火球を移動させると、次のようなことが起こります.
質量は火球質量の和である.
にゅうしゅつりょく
[入力]
1行目はN MK
2行目から、M行ごとにFire Ballの情報が与えられます.火球の情報は5つの整数ri,ci,mi,si,diからなる.
2つの異なる火球の位置が同じである場合、入力は与えられません.
[出力]
法師サメはK回移動を命じた後、残りの火球質量の和を出力する.
[制限]
私の答え
私は問題を見ると容易ではないと思います.
特殊なアルゴリズムを用いた問題に比べて,このように複数の要素の問題を注意深く確認し,正しく処理することはより困難であるようである.
まず、問題は大きく二つの段階に分けられる.
移動火球+マージ火球再分割
また、この問題の特徴は火球に多くの要素があることです.火球の位置を表す座標と質量、速度、移動方向を覚えておいてください.私の場合、質量、速度、移動方向がpropertyの構造体
FireBall
を宣言し、N*N
の大きさの座標をベクトルとする2次元配列FireBall
を管理する.このような資料構造を宣言する方法が有効かどうか疑問だが、入力範囲が大きくないため、簡単で正確な解答方式を選んだ.
移動
回転二次元配列
map
は、その位置する球速度、移動方向を用いて新しい座標に移動し、移動後の球を記録する新しい二次元座標map
に反映される.新しい座標を求める過程で苦労したが,移動速度がNより大きい場合,mod演算によりその値を小さくし,移動方向が境界を超え,移動後の座標値が0より小さいかN−1より大きい場合,処理の部分が重要である.
マージ後に再分割
移動した球の二次元配列
moved
を保存して同じ探索を行った.このとき、座標中のボールが1つしかない場合は、合わせて分けて再反映した
moved
のうち、2つ以上のボールのみが行われる.位置上のボールの質量と速度とを求め、移動方向が一致しているかどうかを確認した後、新しい質量、速度、移動方向を反映して
map
に入れる.このプロセスはK回行われ、最後の実行であれば、最終的に
map
に入ったボールの質量を合わせて返します.ループの座標部分の練習が必要みたい…!
コード#コード#
#include <iostream>
#include <vector>
// boj 20056 마법사 상어아 파이어볼, 골드 5, simulation
using namespace std;
int dy[8] = {-1,-1,0,1,1,1,0,-1};
int dx[8] = {0,1,1,1,0,-1,-1,-1};
typedef struct fireball {
int mass;
int speed;
int dir;
fireball(int m, int s, int d) {
this->mass = m;
this->speed = s;
this->dir = d;
}
}FireBall;
vector<vector<vector<FireBall>>> map;
int moveFireBall(int N, int M, int K){
int left_mass = 0;
int order = K;
while (order>0){
order--;
vector<vector<vector<FireBall>>> moved(N+1, vector<vector<FireBall>>(N+1));
// 1. 이동
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
if (map[i][j].empty()) continue;
for (int k = 0; k < map[i][j].size(); ++k){
int m = map[i][j][k].mass;
int s = map[i][j][k].speed;
int d = map[i][j][k].dir;
int nr = i + dy[d] * (s % N);
if (nr<1) nr += N;
if (nr>N) nr -= N;
int nc = j + dx[d] * (s % N);
if(nc<1) nc += N;
if (nc>N) nc -= N;
moved[nr][nc].push_back(FireBall(m, s, d));
}
}
}
// 2. 정리
map.clear();
map.assign(N+1, vector<vector<FireBall>>(N+1));
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
if (moved[i][j].empty()) continue;
if (moved[i][j].size()==1){
map[i][j].push_back(FireBall(moved[i][j][0].mass,moved[i][j][0].speed,moved[i][j][0].dir));
if(order==0) left_mass += moved[i][j][0].mass;
continue;
}
int mass_sum = 0, speed_sum = 0;
bool same_dir = true;
for (int k = 0; k < moved[i][j].size(); ++k) {
mass_sum += moved[i][j][k].mass;
speed_sum += moved[i][j][k].speed;
if(k>0 && same_dir){
if (moved[i][j][k-1].dir%2 != moved[i][j][k].dir%2) same_dir = false;
}
}
mass_sum = mass_sum/5;
if (mass_sum == 0) continue;
speed_sum = speed_sum/moved[i][j].size();
int dir_base = -1;
if (same_dir) dir_base = 0;
else dir_base = 1;
for (int k = 0; k <=6 ; k += 2) {
map[i][j].push_back(FireBall(mass_sum, speed_sum, dir_base+k));
if (order==0) left_mass += mass_sum;
}
}
}
}
return left_mass;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int N, M, K;
cin>>N>>M>>K;
map.assign(N+1, vector<vector<FireBall>>(N+1));
int r, c, m, s, d;
for (int i = 0; i < M; ++i) {
cin>>r>>c>>m>>s>>d;
map[r][c].push_back(FireBall(m, s, d));
}
cout<<moveFireBall(N,M,K);
return 0;
}
Reference
この問題について([アルゴリズム解析]BOJ 20056法師サメと火球), 我々は、より多くの情報をここで見つけました https://velog.io/@nnnyeong/알고리즘-풀이-분석-BOJ-20056-마법사-상어와-파이어볼テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol