(C++) 백준 17071: 숨바꼭질 5

https://www.acmicpc.net/problem/17071

문제에서 -1, +1, *2,

*2의 경우는 제쳐두고 +-1만 생각하면

t초에 위치 X에 있었다면 여전히 t+2n초에 위치 X로 이동할 수 있습니다.

*2가 있어서 헷갈리지만 *2가 있다고 해서 위의 문장이 틀린 것은 아닙니다.

*2의 추가 규칙은 없습니다.

따라서 일반 BFS와 달리

“t초에 X 위치에 있었다면 t+2n초에 X 위치로 이동할 수 있습니다.” 그것을 사용해야합니다

애초에 BFS를 사용하는 이유는 이 문제에서 더 많이 움직일수록 목표에 더 가까워지기 때문입니다.

목표는 중복을 제거하는 것입니다.

문제는 내 위치에서 중복 항목을 제거할 수 없다는 것입니다.

(답변은 내 위치가 아닌 누나 위치 영향을 받기 때문에)

물론 중복을 제거하지 않으면 시간이 초과됩니다.

그래서 아이디어가 번뜩이는 중복 사례를 찾는 게 관건이다(웃음).

사례는 “t초에 위치 X에 있었다면 여전히 t+2n초에 위치 X로 이동할 수 있습니다.”입니다. 규칙에서 나온다

그러나 아이디어를 내기가 어렵다.

어떻게든 문제에 접근할 방법을 고민하고 있다면

3^(무한) 경우의 수 -> 중복 제거 필수 -> 위치만으로는 중복 제거 불가 -> 어떻게?

생각해보면.. 그에게 다가갈 수 있을지도 몰라

근데 그걸 깨달아도 해결하기가 좀 어려워

주의해야 할 몇 가지 사항이 있습니다.


이 예에서 내가 17 -> 16 -> 15 -> 14 -> 15를 움직이면 4초 안에 동생을 칠 수 있습니다(다른 움직임도 가능).

보면 2초에 15, 4초에 15가 들립니다.

그래서 2초에 15에서 멈췄으니 2초 + 2 * 1초인 4초에 안듣는게 맞다.

그렇게 하면 답장을 할 수 없습니다.

먼저, 원칙적으로 “t초 안에 X의 위치에 있었다면 t+2n초 안에 X의 위치로 갈 수 있다”,

t초 내에 X로 이동하면 t+2n초 내에 이동하면 안 됩니다.

그것은 단지 최적화 일 뿐이며 문제를 해결할 생각조차하지 않았습니다.

아래와 같이 방문이 완료되면

내 현재 위치와 내 동생의 위치가 같은지 확인만 하는 것이 아니라 형제여

내가 이전에 기록했던 방문에서,

형님도 오셨는지 확인이 필요한데, 이 룰에서 찾을 수 있는 핵심 부분입니다.

내 위치가 줄어들고 늘어날 수 있지만 중복될 수 있음

형의 지위는 올라갈 뿐 중복은 절대 없기 때문에

이 문제는 내 입장보다 동생 입장에서 생각하기가 더 쉽다.

그래도

while (!q.empty()) {
    int cur = q.front().first;
    int cnt = q.front().second;
    q.pop();
    int brother = K + (cnt * (cnt + 1) / 2);
    if (cur == brother) return cnt;
    if (cnt & 1 && hol_visit(brother)) return cnt;
    if (!(cnt & 1) && jjak_visit(brother)) return cnt;
	...
}

위와 같이 cur==brother가 아래에 있는지 확인하십시오.

내가 전에 왔는지 확인하기 위해 동생의 행방을 확인하는 것이 절대적으로 필요합니다.

그 외에도 고려해야 할 두 가지 다른 사항이 있습니다.

처음에는

while (!q.empty()) {
    int cur = q.front().first;
    int cnt = q.front().second;
    q.pop();
    int brother = K + (cnt * (cnt + 1) / 2);
    if (brother > 500000) return -1;//이거 조심
    //50만 넘어서도 만나는걸 체크함..
    if (cur == brother) {
        return cnt;
    }
    if (cnt & 1 && hol_visit(brother)) return cnt;
    if (!(cnt & 1) && jjak_visit(brother)) return cnt;
    ...
}

Brother가 500,000건이 넘는 사건을 처리하지 않으면

Cur 자신은 500,000을 넘지 못하기 때문에

if(cur==형제) return cnt; 지속되지는 않지만

아래 두 개의 return 문에 갇힌 것 같습니다(방문이 500,000까지만 0으로 초기화되었기 때문입니다).

그리고 첫째, 형제가 500,000을 초과하는지 볼 필요가 없습니다 (형제는 성장할뿐입니다)

또한 최적화를 위해 -1을 반환합니다. 하기 좋은

두 번째는

while (!q.empty()) {
    ...
    int nxt = cur * 2;//next니까 홀수일때 짝수 visit 체크해주는거 조심
    if (nxt <= 500000 && (cnt & 1 ? !jjak_visit(nxt) : !hol_visit(nxt))) {
        cnt & 1 ? jjak_visit(nxt) = true : hol_visit(nxt) = true;
        q.push({ nxt , cnt + 1 });
    }
    nxt = cur + 1;
    if (nxt <= 500000 && (cnt & 1 ? !jjak_visit(nxt) : !hol_visit(nxt))) {
        cnt & 1 ? jjak_visit(nxt) = true : hol_visit(nxt) = true;
        q.push({ nxt , cnt + 1 });
    }
    nxt = cur - 1;
    if (nxt >= 0 && (cnt & 1 ? !jjak_visit(nxt) : !hol_visit(nxt))) {
        cnt & 1 ? jjak_visit(nxt) = true : hol_visit(nxt) = true;
        q.push({ nxt , cnt + 1 });
    }
}

즉, cnt가 홀수이면 hol_visit를 확인하지 않습니다.

방문을 확인하는 것은 cnt + 1 상태에 있는 다음을 확인하는 것입니다.

cnt가 홀수이면 짝수 방문으로 확인 / cnt가 짝수이면 홀수 방문으로 확인

당신은 할 수 있습니다

조심해야 할 것들

모든 것이 반례로 여기에 있습니다.

https://www.acmicpc.net/board/view/99122

기사 읽기 – 최적화 정크 반례 던지기

댓글을 달려면 로그인이 필요합니다.

www.acmicpc.net

하하 제목이 너무 아프다~

제 기억으로는 500,000이 넘는 형제를 확인하지 않으면 첫 번째 경우에 문제가 있었던 것 같습니다.

두 번째 짝과 헷갈린다면 후자의 경우 문제가 발생한 것으로 보인다.

전체 코드

#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;

int N, K;
bool hol_visit(500001);
bool jjak_visit(500001);

int bfs() {
	queue<pair<int, int>> q;
	q.push({ N, 0 });
	jjak_visit(N) = true;

	while (!q.empty()) {
		int cur = q.front().first;
		int cnt = q.front().second;
		q.pop();
		int brother = K + (cnt * (cnt + 1) / 2);
		if (brother > 500000) return -1;//이거 조심
		//50만 넘어서도 만나는걸 체크함..
		if (cur == brother) {
			return cnt;
		}
		//이것도 조심 ( 핵심임 )
		if (cnt & 1 && hol_visit(brother)) return cnt;
		if (!(cnt & 1) && jjak_visit(brother)) return cnt;

		int nxt = cur * 2;//next니까 홀수일때 짝수 visit 체크해주는거 조심
		if (nxt <= 500000 && (cnt & 1 ? !jjak_visit(nxt) : !hol_visit(nxt))) {
			cnt & 1 ? jjak_visit(nxt) = true : hol_visit(nxt) = true;
			q.push({ nxt , cnt + 1 });
		}
		nxt = cur + 1;
		if (nxt <= 500000 && (cnt & 1 ? !jjak_visit(nxt) : !hol_visit(nxt))) {
			cnt & 1 ? jjak_visit(nxt) = true : hol_visit(nxt) = true;
			q.push({ nxt , cnt + 1 });
		}
		nxt = cur - 1;
		if (nxt >= 0 && (cnt & 1 ? !jjak_visit(nxt) : !hol_visit(nxt))) {
			cnt & 1 ? jjak_visit(nxt) = true : hol_visit(nxt) = true;
			q.push({ nxt , cnt + 1 });
		}
	}
	return -1;
}

int main() {
	scanf("%d%d", &N, &K);
	printf("%d", bfs());
}