알고리즘/알고리즘(Java)

23. [다이나믹]옷

산을좋아한라쯔 2015. 4. 24. 22:00
반응형

문제
옷을 정가 이하로 바겐세일 하는 가게에 갔다.
그 가게에서, 현재 가지고 있는 현금(P) 이내에서, 최대 가치가되게(=정가 기준 합이 가장 크게) 옷을 구매하는 방법을 제시하시오.
단, 구매할 수 있는 옷의 수량 한도내에서 구매해야 함(L)
  - 가치: 옷의 정가 (values)
  - 판매가격: 현재 옷의 가격 (prices)

 

목표: 전체 구매한 옷의 정가 합이 최대가 되도록
제약조건: 가지고 있는 현금 이내라야함. P >= Sum(옷의 판매가격) 
          구매한도 개수 이내라야 함. L >= 구매한 옷의 수량
         
입력파일(Clothes.in)
<형태>

문제개수(T)
옷_개수(N) 보유현금(P) 개수한계(L)
판가1 판가2 ... 판가(N)
정가1 정가2 ... 정가(N)

 

8

4 12 4

5 4 6 3

10 40 30 50

4 12 2

5 4 6 3

10 40 30 50

3 30 3

5 10 20

50 60 140

3 50 3

10 20 30

60 100 120

5 20 5

2 3 4 5 9

3 4 5 8 10

30 60 30

2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9   

3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20

30 60 20

2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9   

3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20

50 100 50

2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9 2 3 4 5 9   

3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20 3 4 5 8 10 4 6 11 2 20



출력 형태

Test #1

구매 옷 리스트:4 2 1 

총 구매 가격:12

총 정가:100

Elapsed:1ms     


작성방법

제시된 Clothes.java를 기초로해서 작성하시오.

수행시간: 50ms 이내.

//Clothes.java

package dynamic;

 

import java.io.File;

import java.util.ArrayList;

import java.util.Scanner;

 

public class Clothes {

         public static void main(String[] args) throws Exception {

                  long t1, t2;

 

                  Scanner sc = new Scanner(new File("res/Clothes.in"));

 

                  int T;

                  T = sc.nextInt();

 

                  int N = 0; // 개수

                  int P = 0; // 보유현금

                  int L = 0; // 구매가능한 개수

                  int[] prices = null; // 판가

                  int[] values = null; // 정가

                  for (int testNum = 1; testNum <= T; testNum++) {

                           N = sc.nextInt();

                           P = sc.nextInt();

                           L = sc.nextInt();

 

                           prices = new int[N + 1]; // 판가

                           for (int i = 1; i <= N; i++) {

                                   prices[i] = sc.nextInt();

                           }

 

                           values = new int[N + 1]; // 정가

                           for (int i = 1; i <= N; i++) {

                                   values[i] = sc.nextInt();

                           }

 

                           t1 = System.currentTimeMillis();

                           int[] purchaseList = getPurchaseList(N, P, L, prices, values);

                           t2 = System.currentTimeMillis();

 

                           System.out.println("Test #" + testNum);

                           printPurchaseList(purchaseList, prices, values);

                           System.out.println("Elapsed:" + (t2 - t1) + "ms");

                           System.out.println("");

                  }

                  sc.close();

         }

 

         private static void printPurchaseList(int[] list, int[] prices, int[] values) {

                  if (list == null || list.length == 0) {

                           System.out.println("fail");

                           return;

                  }

 

                  StringBuffer sb = new StringBuffer();

                  long totalPurchasingPrice = 0;

                  long totalValue = 0;

                  for (int i = 0; i < list.length; i++) {

                           sb.append(list[i] + " ");

                           totalPurchasingPrice += prices[list[i]];

                           totalValue += values[list[i]];

                  }

 

                  System.out.println("구매 리스트:" + sb.toString());

                  System.out.println(" 구매 가격:" + totalPurchasingPrice);

                  System.out.println(" 정가:" + totalValue);

         }

 

         private static int[] getPurchaseList(int n, int p, int l, int[] prices, int[] values) {

                 

 

                  return null;

         }

}



--------------------------------------------------------------------------------------

***********************************************************************************************************

--------------------------------------------------------------------------------------


풀이

배낭문제를 응용해서 만든 문제이다. 따라서, 배낭문제를 잘 이해할 수 있다면, 쉽게 풀 수 있는 문제이다. 


배낭문제의 경우는, 주어진 보석들에 대해서, 허용된 무게만큼의, 최대가치가 되는 보석을 선택하는 것이다.


옷 문제는, 주어진 옷들에 대해서, 허용된 가격만큼의, 허용된 개수 만큼의, 최대가치(=정가)가 되는 옷을 선택하는 것이다.

조건이 하나 더 늘어난 것이다.


점화식을 만들어 보자.

최대가치를 나타내는 함수를 V라하고, 옷의 개수는 i, 허용된 가격은 p, 허용된 개수는 l 이라 하면,

V(i,p,l) = (p >= prices[i] && l >= 1) ? max(V(i-1,p,l),V(i-1,p-prices[i],l-1)+val[i]) : V(i-1,w,l)


참조로, 배낭문제에서의 점화식과 비교해보면 이해가 될 것이다.

V(i,w) = (w >= wt[i]) ? max(V(i-1,w), V(i-1,w-wt[i]) + val[i]) : V(i-1,w)


따라서, 이를 구현하면 다음과 같이 된다. (먼저 재귀형태로 생각해보고, 재귀형태의 호출이 중복되지 않도록 메모이제이션을 하는 순으로 작성해야 한다. )

         private static int purchase_table(int n, int p, int l,

int[] prices, int[] values, int[][][] table) {

                  if (n <= 0 || p <= 0 || l <= 0) {

                           return 0;

                  }

 

                  if ((n > 1) && (table[n - 1][p][l] == 0)) {

                           table[n - 1][p][l] = purchase_table(n - 1, p, l, prices, values, table);

                  }

 

                  if ((n > 1 && p > prices[n] && l > 1) && (table[n - 1][p - prices[n]][l - 1] == 0)) {

                           table[n - 1][p - prices[n]][l - 1] =

purchase_table(n - 1, p - prices[n], l - 1, prices, values, table);

                  }

 

                  if ((prices[n] <= p && l >= 1) &&

(table[n - 1][p][l] < table[n - 1][p - prices[n]][l - 1] + values[n])) {

                           table[n][p][l] = table[n - 1][p - prices[n]][l - 1] + values[n];

                  } else {

                           table[n][p][l] = table[n - 1][p][l];

                  }

                  return table[n][p][l];

         }


얻어진 table로부터, 어떤 옷이 선택되었는 지를 트레이싱하는 것은 다음과 같다.

         private static int[] extract(int[][][] V, int P, int L, int[] prices) {

                  ArrayList<Integer> list = new ArrayList<Integer>();

 

                  int p = P;

                  int l = L;

                  for (int i = V.length - 1; i >= 1; i--) {

                           if (p >= 0 && l >= 0 && V[i][p][l] != V[i - 1][p][l]) {

                                   list.add(i);

                                   p = p - prices[i];

                                   l = l - 1;

                           }

                  }

 

                  int[] a = new int[list.size()];

                  for (int i = 0; i < a.length; i++) {

                           a[i] = list.get(i);

                  }

 

                  return a;

         }


전체소스는 아래와 같고, 여기에는 위와 같이 메모이제이션을 이용한 방법과 함께, bottom up으로 테이블 전체값을 얻어내서 구하는 방식 모두 구현되어 있다.


소스

package dynamic;

 

import java.io.File;

import java.util.ArrayList;

import java.util.Scanner;

 

public class Clothes {

         public static void main(String[] args) throws Exception {

                  long t1, t2;

 

                  Scanner sc = new Scanner(new File("res/Clothes.in"));

 

                  int T;

                  T = sc.nextInt();

 

                  int N = 0; // 개수

                  int P = 0; // 보유현금

                  int L = 0; // 구매가능한 개수

                  int[] prices = null; // 판가

                  int[] values = null; // 정가

                  for (int testNum = 1; testNum <= T; testNum++) {

                           N = sc.nextInt();

                           P = sc.nextInt();

                           L = sc.nextInt();

 

                           prices = new int[N + 1]; // 판가

                           for (int i = 1; i <= N; i++) {

                                   prices[i] = sc.nextInt();

                           }

 

                           values = new int[N + 1]; // 정가

                           for (int i = 1; i <= N; i++) {

                                   values[i] = sc.nextInt();

                           }

 

                           t1 = System.currentTimeMillis();

                           int[] purchaseList = getPurchaseList(N, P, L, prices, values);

                           t2 = System.currentTimeMillis();

 

                           System.out.println("Test #" + testNum);

                           printPurchaseList(purchaseList, prices, values);

                           System.out.println("Elapsed:" + (t2 - t1) + "ms");

                           System.out.println("");

                  }

                  sc.close();

         }

 

         private static void printPurchaseList(int[] list, int[] prices, int[] values) {

                  if (list == null || list.length == 0) {

                           System.out.println("fail");

                           return;

                  }

 

                  StringBuffer sb = new StringBuffer();

                  long totalPurchasingPrice = 0;

                  long totalValue = 0;

                  for (int i = 0; i < list.length; i++) {

                           sb.append(list[i] + " ");

                           totalPurchasingPrice += prices[list[i]];

                           totalValue += values[list[i]];

                  }

 

                  System.out.println("구매 리스트:" + sb.toString());

                  System.out.println(" 구매 가격:" + totalPurchasingPrice);

                  System.out.println(" 정가:" + totalValue);

         }

 

         private static int[] getPurchaseList(int n, int p, int l, int[] prices, int[] values) {

                  int[][][] V = new int[n + 1][p + 1][l + 1];

 

                  //purchase(n, p, l, prices, values, V);

                  purchase_table(n, p, l, prices, values, V);

 

                  return extract(V, p, l, prices);

         }

 

         private static int[] extract(int[][][] V, int P, int L, int[] prices) {

                  ArrayList<Integer> list = new ArrayList<Integer>();

 

                  int p = P;

                  int l = L;

                  for (int i = V.length - 1; i >= 1; i--) {

                           if (p >= 0 && l >= 0 && V[i][p][l] != V[i - 1][p][l]) {

                                   list.add(i);

                                   p = p - prices[i];

                                   l = l - 1;

                           }

                  }

 

                  int[] a = new int[list.size()];

                  for (int i = 0; i < a.length; i++) {

                           a[i] = list.get(i);

                  }

 

                  return a;

         }

 

         private static int purchase_table(int n, int p, int l, int[] prices,

int[] values, int[][][] table) {

                  if (n <= 0 || p <= 0 || l <= 0) {

                           return 0;

                  }

 

                  if ((n > 1) && (table[n - 1][p][l] == 0)) {

                           table[n - 1][p][l] = purchase_table(n - 1, p, l, prices, values, table);

                  }

 

                  if ((n > 1 && p > prices[n] && l > 1) && (table[n - 1][p - prices[n]][l - 1] == 0)) {

                           table[n - 1][p - prices[n]][l - 1] =

purchase_table(n - 1, p - prices[n], l - 1, prices, values, table);

                  }

 

                  if ((prices[n] <= p && l >= 1) &&

(table[n - 1][p][l] < table[n - 1][p - prices[n]][l - 1] + values[n])) {

                           table[n][p][l] = table[n - 1][p - prices[n]][l - 1] + values[n];

                  } else {

                           table[n][p][l] = table[n - 1][p][l];

                  }

                  return table[n][p][l];

         }

 

         private static int purchase(int N, int P, int L, int[] prices, int[] values, int[][][] V) {

                  for (int i = 1; i <= N; i++) {

                           for (int j = 0; j <= P; j++) {

                                   for (int k = 1; k <= L; k++) {

                                            if ((prices[i] <= j) && 

                                                (V[i - 1][j][k] < V[i - 1][j - prices[i]][k - 1] + values[i])) {

                                                     V[i][j][k] = V[i - 1][j - prices[i]][k - 1] + values[i];

                                            } else {

                                                     V[i][j][k] = V[i - 1][j][k];

                                            }

                                   }

                           }

                  }

                  return V[N][P][L];

         }

 

}

 


실행결과

Test #1

구매 옷 리스트:4 2 1 

총 구매 가격:12

총 정가:100

Elapsed:0ms


Test #2

구매 옷 리스트:4 2 

총 구매 가격:7

총 정가:90

Elapsed:0ms


Test #3

구매 옷 리스트:3 2 

총 구매 가격:30

총 정가:200

Elapsed:0ms


Test #4

구매 옷 리스트:3 2 

총 구매 가격:50

총 정가:220

Elapsed:0ms


Test #5

구매 옷 리스트:5 4 3 1 

총 구매 가격:20

총 정가:26

Elapsed:0ms


Test #6

구매 옷 리스트:30 28 27 26 21 20 18 17 16 11 10 8 7 6 1 

총 구매 가격:60

총 정가:132

Elapsed:3ms


Test #7

구매 옷 리스트:30 28 27 26 21 20 18 17 16 11 10 8 7 6 1 

총 구매 가격:60

총 정가:132

Elapsed:2ms


Test #8

구매 옷 리스트:50 48 47 46 40 38 37 36 30 28 27 26 20 18 17 16 14 10 8 7 6 4 

총 구매 가격:100

총 정가:221

Elapsed:13ms



-끝

반응형