kil_alpaca
기록하는 습관
kil_alpaca
전체 방문자
오늘
어제
  • 분류 전체보기 (117)
    • 잡다한 오류 (2)
    • 머신러닝 & 딥러닝 (4)
      • 이론 (2)
      • 통계 기법 (2)
      • 잡다한 오류 (0)
    • Pytorch (6)
    • Tensorflow 2 (1)
      • 오류 처리 등등 (1)
    • Ubuntu (3)
      • 이것저것 (2)
      • 오류 처리 등등 (1)
    • Problem Solving (93)
      • Dynamic Programming (52)
      • Convex Hull Trick (8)
      • Dijkstra (33)
    • 자료구조 (3)
      • 기본 자료구조 (2)
      • Tree (1)
    • 알고리즘 (5)
      • 기하학 (4)
      • 최적화 기법 (1)

블로그 메뉴

  • 홈

공지사항

인기 글

태그

  • 자료구조
  • pyTorch
  • 데이크스트라
  • DP optimization
  • 알고리즘
  • Tensor
  • 텐서
  • Convex Hull Trick
  • 파이토치
  • Dijkstra
  • 통계
  • convolution layer
  • kaiming 초기화
  • 그래프 탐색
  • dynamic programming
  • 백준
  • DP
  • 머신러닝
  • 그래프 이론
  • 다익스트라
  • 딥러닝
  • glorot 초기화
  • 기하학
  • 통계기법
  • he 초기화
  • Convex Hull Optimization
  • convex hull
  • CCW
  • CHT
  • Li Chao Tree

최근 글

티스토리

hELLO · Designed By 정상우.
kil_alpaca

기록하는 습관

Problem Solving/Dynamic Programming

[백준] 2629번: 양팔저울

2023. 2. 20. 19:47

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

 

2629번: 양팔저울

첫째 줄에는 추의 개수가 자연수로 주어진다. 추의 개수는 30 이하이다. 둘째 줄에는 추의 무게들이 자연수로 가벼운 것부터 차례로 주어진다. 같은 무게의 추가 여러 개 있을 수도 있다. 추의 무

www.acmicpc.net


가능 여부를 확인해서 해결하는 dp 문제다.

 

1. 문제 조건

1) 양팔 저울의 왼쪽에 구슬이 있는데, 주어진 추를 사용해서 구슬의 무게를 잰다.

2) 추와 구슬의 무게가 주어졌을 때, 그 구슬의 무게를 잴 수 있는지 여부를 출력한다.

3) 추는 왼쪽 또는 오른쪽에 올릴 수 있다.

4) 추를 올리지 않을 수도 있다.

5) 추의 개수는 최대 30, 무게는 최대 500 이하의 자연수다.

 

2. dp 배열 정의

추의 사용 여부와 올리는 위치에 따라

다양한 경우가 생겨나서 조금 생각하기 어려웠다.

 

추를 주어진 순서대로 번호를 매겼을 때

어떤 특정한 무게를 만들 수 있는지 여부를 dp 배열로 저장하면 된다.

#define MAXN 31
#define MAXW 15001	// 30 X 500

// dp[n][w] = n번째 추까지 왔을 때 w를 만들 수 있다면 true, 아니라면 false
int dp[MAXN][MAXW];

// weight[n] = n번째 추의 무게
int weight[MAXN];

 

3. 해결법

추의 사용 조건을 생각하면 총 3가지 경우가 나온다.

구슬은 항상 왼쪽에 있다고 가정한다.

 

1) 추를 사용하지 않는다.

2) 추를 오른쪽에 올린다.

3) 추를 왼쪽(구슬)에 올린다.

 

위 3가지 경우에 대해 재귀 함수를 사용해서

DFS 방식으로 dp 배열을 만들면 된다.

 

1) N < n (base)

- 현재 확인하는 n번째 추가 전체 추의 개수보다 크면 재귀함수를 끝낸다.

2) dp[n][w] == true (base)

- 현재 확인하는 n번째 추까지 만들 수 있는 무게가 이미 가능하다고 기록되어 있다면, 재귀함수를 끝낸다.

3) 그 외

- 재귀함수 안에 들어왔으니 무게 w를 만들 수 있다. 따라서 dp[n][w] = true 로 기록한다.

- 추를 사용하지 않는 경우, 무게를 변화시키지 않고 재귀함수를 호출한다.

- 추를 오른쪽에 올리는 경우, 현재 무게에 weight[n] 을 더해서 재귀함수를 호출한다.

- 추를 왼쪽에 올리는 경우, 현재 무게에 weight[n] 를 뺀 값의 절댓값으로 재귀함수를 호출한다.

 

재귀함수를 호출할 때는 당연히 n을 1 증가시켜서 인자로 넘겨주어야 한다.

왜냐하면 다음 추를 본다는 뜻이기 때문이다.

 

추를 오른쪽에 올리면, 내가 측정할 구슬의 무게가 더 늘어나게 된다.

따라서 현재 무게에 weight[n] 을 더해서 재귀호출의 인자로 넘겨준다.

 

추를 왼쪽에 올리면, 실질적으로 내가 측정할 구슬의 무게가 줄어들게 된다.

따라서 현재 무게에 weight[n] 을 뺀 값을 재귀호출의 인자로 넘겨주어야 한다.

그런데 weight[n] 을 뺀 결과가 음수가 나올 수 있다.

이 때는 단순히 지금까지 올려 놓은 추의 위치를 서로 바꾸어준다고 생각하면 된다.

따라서 현재 무게에 weight[n] 을 뺀 값의 절댓값을 재취호출의 인자로 넘겨주어야 한다.

 

코드로 보면 다음과 같다.

void solve(int n, int w) {
	if(n > N || dp[n][w]) return;	// 사용 가능한 추 개수를 넘거나 이미 결정난 경우

	dp[n][w] = true;

	solve(n+1, w);					// 1) 추를 올리지 않는 경우
	solve(n+1, w + weight[n]);		// 2) 추를 오른쪽에 올리는 경우
	solve(n+1, abs(w - weight[n]));	// 3) 추를 왼쪽에 올리는 경우
}

전체 풀이 코드는 다음과 같다.

#include <iostream>
using namespace std;

#define MAXN 31
#define MAXW 15001

bool dp[MAXN][MAXW];
int weight[MAXN];

int N;

inline int abs(int n) { return (n > 0) ? n : -n; }

void solve(int n, int w) {
	if(n > N || dp[n][w]) return;

	dp[n][w] = true;

	solve(n+1, w);
	solve(n+1, w + weight[n]);
	solve(n+1, abs(w - weight[n]));
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N;
	for(int i=0; i<N; i++) cin >> weight[i];

	solve(0, 0);

	int T; cin >> T;
	while(T--) {
		int w; cin >> w;

		if(w >= MAXW) cout << "N ";
		else		  cout << (dp[N][w] ? "Y " : "N ");
	}

	return 0;
}
저작자표시 비영리 변경금지 (새창열림)

'Problem Solving > Dynamic Programming' 카테고리의 다른 글

[백준] 17090번: 미로 탈출하기  (0) 2023.02.22
[백준] 1937번: 욕심쟁이 판다  (0) 2023.02.20
[백준] 1958번: LCS 3  (0) 2023.02.20
[백준] 11066번: 파일 합치기  (0) 2023.02.19
[백준] 14846번: 직사각형과 쿼리  (0) 2023.02.18
    'Problem Solving/Dynamic Programming' 카테고리의 다른 글
    • [백준] 17090번: 미로 탈출하기
    • [백준] 1937번: 욕심쟁이 판다
    • [백준] 1958번: LCS 3
    • [백준] 11066번: 파일 합치기
    kil_alpaca
    kil_alpaca

    티스토리툴바