단순한 시뮬레이션 문제라 판단했다.
매 차례(문제에서는 연쇄라 일컫는다)를 play()
메서드로 처리한다. 이 메서드 내에서는 다음과 같은 처리를 수행한다
pangpang
메서드를 수행한다gravity
static boolean play() {
boolean success = false;
for (int i =0; i < H; i++) {
for (int j = 0; j < W; j++) {
boolean[][] visited = new boolean[H][W];
if (grid[i][j] != '.') {
if (pangpang(i, j, visited)) {
success = true;
}
}
}
}
gravity();
return success;
}
static boolean pangpang(int startY, int startX, boolean[][] visited) {
char color = grid[startY][startX];
ArrayDeque<int[]> sameColor = new ArrayDeque<>();
List<int[]> result = new ArrayList<>();
sameColor.add(new int[] {startY, startX});
visited[startY][startX] = true;
result.add(new int[]{startY, startX});
while (!sameColor.isEmpty()) {
int[] node = sameColor.pollFirst();
int y = node[0];
int x = node[1];
for (int d = 0; d < 4; d++) {
int ny = DY[d] + y;
int nx = DX[d] + x;
if (isValidCoordinate(ny, nx) && !visited[ny][nx] && color == grid[ny][nx]) {
visited[ny][nx] = true;
result.add(new int[] {ny, nx});
sameColor.add(new int[] {ny, nx});
}
}
}
if (result.size() >= 4) {
for (int[] coordinate : result) {
int y = coordinate[0];
int x = coordinate[1];
grid[y][x] = '.';
}
return true;
}
return false;
}
'.'이 아닌 모든 셀들을 덱에 순차적으로 더한 다음, 덱의 마지막 요소부터, 컬럼의 맨 아래 칸부터 채워간다.
static void gravity() {
char[][] newGrid = new char[H][W];
newGrid = initializeGrid(newGrid);
for (int col = 0; col < W; col++) {
ArrayDeque<Character> chars= new ArrayDeque<>();
for (int row = 0; row < H; row++) {
if (grid[row][col] != '.') {
chars.add(grid[row][col]);
}
}
int row = H-1;
while (!chars.isEmpty()) {
newGrid[row--][col] = chars.pollLast();
}
}
grid = newGrid;
}
int count = 0;
while (play()) {
count++;
}
output.append(count);
cycle을 보고 Prim 알고리즘을 떠올렸다. Prim 알고리즘은 a 와 b가 같은 집합에 속하는지 확인한 다음 union하기 때문에 사이클 발생 여부를 확인하기 편리하다.
static void make() {
parents= new int[n];
size = new int[n];
Arrays.fill(parents, -1);
Arrays.fill(size, 1);
}
static int findSet(int a) {
if (parents[a] < 0) return a;
return parents[a] = findSet(parents[a]);
}
static boolean union(int a, int b){
int aRoot = findSet(a);
int bRoot = findSet(b);
if (aRoot == bRoot) return false;
if (size[bRoot] > size[aRoot]) {
size[bRoot] += size[aRoot];
parents[aRoot] = bRoot;
} else {
size[aRoot] += size[bRoot];
parents[bRoot] = aRoot;
}
return true;
}
int count = 1;
make();
int[][] edges = new int[m][2];
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int v1 = Integer.parseInt(st.nextToken());
int v2 = Integer.parseInt(st.nextToken());
edges[i][0] = v1;
edges[i][1] = v2;
}
for (int[] edge : edges) {
if (!union(edge[0], edge[1])) break;
count++;
}
sb.append(count == m+1 ? 0 : count);
일단 정석대로 코드를 완성한 다음 꼼수를 부리자
'경우의 수'를 보는 순간 DP 접근이 필요하다는 생각이 들었다.
dp[i][0] = 1;
금액 j에서 현재 동전 금액 m을 뺀 값(j-m
)이 동전들에 의해 만들어질 수 있는 값이라면 (dp[i][j-m] > 0
), j-m
을 만들 수 있는 가짓수를 현재 dp[i][j]
에 더한다.
import java.util.*;
import java.io.*;
public class Main {
static int N, coins[], M;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringBuilder sb = new StringBuilder();
StringTokenizer st = new StringTokenizer(br.readLine());
int T = Integer.parseInt(st.nextToken());
for (int t = 1; t <= T; t++) {
N = Integer.parseInt(br.readLine());
coins = new int[N];
st = new StringTokenizer(br.readLine());
for (int i = 0; i < N; i++) {
coins[i] = Integer.parseInt(st.nextToken());
}
M = Integer.parseInt(br.readLine());
int[][] dp = new int[N][M+1];
for (int i = 0; i < N; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= M; i++) {
if (i % coins[0] == 0) dp[0][i] = 1;
}
for (int coinIdx = 1; coinIdx < N; coinIdx++) {
for (int totalMoney = 0; totalMoney <= M; totalMoney++) {
dp[coinIdx][totalMoney] = dp[coinIdx-1][totalMoney];
if (totalMoney - coins[coinIdx] >= 0 && dp[coinIdx][totalMoney - coins[coinIdx]] > 0) {
dp[coinIdx][totalMoney] += dp[coinIdx][totalMoney - coins[coinIdx]];
}
}
}
sb.append(dp[N-1][M]).append("\n");
}
bw.write(sb.toString());
bw.flush();
br.close();
bw.close();
}
}
솔직히 DP는 아직도 모르겠다. 골드 수준 DP는 거의 처음 풀어본 것 같은데 가장 어려웠다. 감도 안온다. 이걸 어쩌냐 ㅋㅋㅋㅋ큐ㅠㅠㅠㅠ
문제 설명이 직관적이라 스킵한다
일단 총감독관은 꼭 1명 있어야 한다. 부감독은 0명 이상 존재한다.
long sum = 0;
for (int i = 0; i < N; i++) {
sum += 1;
if (takers[i] - B > 0) sum += (long) Math.ceil((double)(takers[i] - B)/(double)C);
}
마이너스 값에 대해 나눗셈하면 마이너스 값이 나온다.ㅋㅋㅋ
단순한 시뮬레이션 문제다.
static class Shark {
int size, fishEaten, y, x;
Shark(int y, int x) {
this.y = y;
this.x = x;
this.size = 2;
this.fishEaten = 0;
}
}
static class Fish {
int y, x, distance;
Fish(int y, int x, int distance) {
this.y = y;
this.x = x;
this.distance = distance;
}
}
int[][] visited
배열에 셀까지의 최단 경로를 저장한다
static Fish bfs() {
ArrayDeque<int[]> queue = new ArrayDeque<>();
int[][] visited = new int[N][N];
queue.add(new int[] {babyShark.y, babyShark.x});
visited[babyShark.y][babyShark.x] = 1;
int minDist = Integer.MAX_VALUE;
List<int[]> ans = new ArrayList<>();
while (!queue.isEmpty()) {
int[] node = queue.pollFirst();
int y = node[0];
int x = node[1];
if (visited[y][x] > minDist) break;
if (isConsumable(y, x)) {
minDist = Math.min(minDist, visited[y][x]);
if (visited[y][x]==minDist) ans.add(new int[]{y, x});
}
for (int d = 0; d < 4; d++) {
int ny = y + DY[d];
int nx = x + DX[d];
if (isValidCoordinate(ny, nx) && visited[ny][nx] == 0 && grid[ny][nx] <= babyShark.size) {
visited[ny][nx] = visited[y][x] + 1;
queue.add(new int[] {ny, nx});
}
}
}
if (ans.isEmpty()) return new Fish(-1, -1, -1);
else {
Collections.sort(ans, new Comparator<int[]>() {
@Override
public int compare(int[] a, int[] b) {
if (a[0] != b[0]) return Integer.compare(a[0], b[0]);
return Integer.compare(a[1], b[1]);
}
});
return new Fish(ans.get(0)[0], ans.get(0)[1], minDist-1);
}
}
static void simulate() {
Fish nextFish = bfs();
if (nextFish.y==-1) return;
time += nextFish.distance;
grid[nextFish.y][nextFish.x] = 0;
babyShark.fishEaten++;
babyShark.y = nextFish.y;
babyShark.x = nextFish.x;
if (babyShark.fishEaten == babyShark.size) {
babyShark.size++;
babyShark.fishEaten=0;
}
simulate();
}
시뮬레이션 문제는 언제나 문제를 잘 읽어야 한다
또 시뮬레이션 문제다
int[][] grid
에 물고기의 인덱스를 담아 (i,j) 좌표에 위치하는 물고기의 인덱스를 알 수 있게끔 한다.
이 인덱스로 Fish[] school
를 접근하면 물고기의 정보를 알 수 있다.
static class Shark {
int y, x, direction;
Shark(int y, int x, int direction) {
this.y = y;
this.x =x;
this.direction =direction;
}
}
static class Fish {
int y, x, idx, direction;
Fish(int idx, int direction, int y, int x) {
this.idx = idx;
this.direction = direction;
this.y = y;
this.x = x;
}
}
물고기는 항상 16마리로 정의되어 있고, 1부터 16의 고유번호를 갖기 때문에 Fish[] school = new Fish[16 + 1]
에 담을 수 있다.
static class Fish {
int y, x, idx, direction;
Fish(int idx, int direction, int y, int x) {
this.idx = idx;
this.direction = direction;
this.y = y;
this.x = x;
}
}
static void simulate(int[][] grid, Fish[] school) {
Shark shark = new Shark(0, 0, school[grid[0][0]].direction);
int initialFish = grid[0][0];
school[grid[0][0]] = null;
grid[0][0] = 0;
moveFish(grid, school, shark);
moveShark(initialFish, grid, school, shark);
}
물고기의 이동
static void moveFish(int[][] grid, Fish[] school, Shark shark) {
for (int i = 1; i <= 16; i++) {
if (school[i] == null) continue;
Fish fish = school[i];
for (int d = 0; d < 8; d++) {
int nextDirection = nextDir(fish.direction, d);
int ny = fish.y + DY[nextDirection];
int nx = fish.x + DX[nextDirection];
if (isValidCoordinate(ny, nx) && !sharkIsHere(ny, nx, shark)) {
int tmp = grid[ny][nx];
grid[ny][nx] = fish.idx;
grid[fish.y][fish.x] = tmp;
if (tmp != 0) {
school[tmp].y = fish.y;
school[tmp].x = fish.x;
}
school[i].direction = nextDirection;
school[i].y = ny;
school[i].x = nx;
break;
}
}
}
}
상어의 이동 (DFS)
static void moveShark(int fishEaten, int[][] grid, Fish[] school, Shark shark) {
totalFish = Math.max(fishEaten, totalFish);
for (int dist = 1; dist <= 4; dist++) {
int ny = shark.y + dist*DY[shark.direction];
int nx = shark.x + dist*DX[shark.direction];
if (isValidCoordinate(ny, nx) && grid[ny][nx] > 0) {
Fish eatenFish = school[grid[ny][nx]];
Shark newShark = new Shark(ny, nx, eatenFish.direction);
int[][] newGrid = duplicateGrid(grid);
Fish[] newSchool = duplicateSchool(school);
newGrid[ny][nx] = 0;
newSchool[eatenFish.idx] = null;
moveFish(newGrid, newSchool, newShark);
moveShark(fishEaten + grid[ny][nx], newGrid, newSchool, newShark);
}
}
}