백준

백준(1082번) - 방 번호(Java) (그리디, DP)

kjih1104 2024. 3. 15. 16:39

 DP와 그리디가 섞인 문제이다.

 

처음에는 뒷 숫자부터 무작정 앞으로 갖다 붙이면 될 줄 알았다.

import java.util.*;
import java.io.*;
import java.math.*;


public class Main
{
	public static void main (String[] args) throws IOException
	{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		int[] price = new int[n];
		for(int i = 0 ; i < n ; i++)
		{
			price[i] = Integer.parseInt(st.nextToken());
		}
		
		int m = Integer.parseInt(br.readLine());
		StringBuilder ans = new StringBuilder();
		int index = n-1;
		
		while(index >= 0)
		{
			if(price[index] > m)
			{
				index--;
			}
			else
			{
				ans.append(index);
				m -= price[index];
			}
		}
		
		System.out.println(ans);
	}

}

 뭔 이런 간단한 문제가 다 있나 라는 거만한 마인드로 했다가 대차게 까였다.


 그래서 DP적(?)으로 생각해 각 금액에서 만들 수 있는 최댓값의 번호판을 찾는 방식으로 해결했다.

 

3
6 7 8
21

이 1번의 케이스로 설명을 하자면

 

< 6원 0 / 7원 1 / 8원 2 > 이렇게 만들 수 있다는 건 바로 안다.

이걸 최대 21원까지 생각해 준다면

12원 00

13원 01 / 10 (큰 숫자인 10을 살린다.)

14원 11 / 02 / 20 (큰 숫자인 20을 살린다.)

 

이런 식으로 각 금액에서 만들 수 있는 조합을 전부 생각하고 그중 최강의 숫자만 살려 또 번호를 뒤에 붙이고 또 최강만 남기고...

이것의 반복으로 작성해 봤다.

<문제>

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

 

1082번: 방 번호

첫째 줄에 N이 주아진다. 둘째 줄에는 공백으로 구분된 P0, ..., PN-1이 주어진다. 마지막 줄에는 M이 주어진다.

www.acmicpc.net


<소스코드>

import java.util.*;
import java.io.*;
import java.math.*;


public class Main
{
	public static void main (String[] args) throws IOException
	{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		int[] price = new int[n];
		for(int i = 0 ; i < n ; i++)
		{
			price[i] = Integer.parseInt(st.nextToken());
		}
		
		int m = Integer.parseInt(br.readLine());

		String[] dp = new String[m+1];	//dp의 인덱스를 m(총 금액)으로 세팅한다.
		String ans = "0";
		Arrays.fill(dp, "-1");	//초기값 -1으로 세팅 (세팅 없을 시 null 그러면 골치가 살짝 아파짐)
		
		for(int i = 0 ; i < n ; i++)
		{
			if(price[i] > m) continue;
			
			//일단 초기에 price[i](금액)으로 맞출 수 있는 번호 넣기
			dp[price[i]] = Integer.toString(i);	
		}
		
		for(int i = 0 ; i <= m ; i++)
		{
			if(dp[i] != "-1")	//"-1"은 쓰레기 값으로 무시
			{
				for(int j = 0 ; j < n ; j++)
				{
					if(i + price[j] <= m) //허용 금액 초과하지 않는 선에서
					{
						//만들 수 있는 번호의 조합
						String newString = dp[i] + Integer.toString(j);
                        
						BigInteger num1 = new BigInteger(newString);
						BigInteger num2 = new BigInteger(dp[i + price[j]]);
			
						//9가 50개 들어갈 수 있어서 문자열로 선언했지만 비교는 숫자로 해줘야하는...
						int result = num1.compareTo(num2);
                        
						if(result > 0) 
						{
							//만들 수 있는 숫자가 기존 숫자보다 큰 경우 스위칭
							dp[i + price[j]] = newString;
						}
					}
				}
			}
		}
		
		for(String s : dp)	//다 뒤져보면서 최상의 경우의 수 선택
		{
			BigInteger num1 = new BigInteger(s);
			BigInteger num2 = new BigInteger(ans);
			
			int result = num1.compareTo(num2);
			
			if(result > 0)
				ans = s;
		}
		
		System.out.println(ans);
	}

}