알고리즘/알고리즘 노트

[코드모음]-DP

산을좋아한라쯔 2016. 8. 19. 16:39
반응형

/* DP. 1로 만들기  3가지 연산(나누기3, 나누기2, 빼기1)이용 1 만들기 최소연산횟수  */

D[i]: 숫자 i에 대해서 최소 연산 횟수

D[i] = min(D[i-1]+1, (i%2==0) ? D[i/2], (i%3==0) ? D[i/3])

D[1]=1, D[2]=1, D[3]=1

 

/* DP. 피보나치 */

D[0] = 0

D[1] = 1

D[i] = D[n-2] + D[n-1]


/* DP. 파스칼의 삼각형 */

D[i][j] = (j==0 || j==i) ? 1 : D[i-1][j-1] + D[i-1][j]

 

/* DP. LCS Longest Common Sequence */

Xm=<x1 x2 ... xm> Yn=<y1 y2 yn>

D[i][j] : X i번째 길이까지, Y j번째 길이 까지의 LCS 길이

D[i][j] = D[i-1][j-1] + 1, when xi == yi

         = max(D[i-1][j], D[i][j-1], when xi != yj

D[i][0] = 0, D[0][j]=0

 

/* DP. 집합 A,B에서 하나씩 빼서, 가장 최소차 합 구하기 */

- 두 집합을 소팅

- 같이 차례대로 빼내야 함.

  D[i][j] : A에서 1~i까지, B에서 1~j까지 사용했을 때, 최소 합

  D[i][j] = D[i-1][j-1] + abs(A[i] - B[j])

  if(i>j && D[i][j] > D[i-1][j]) D[i][j] = D[i-1][j]

  if(i<j && D[i][j] > D[i][j-1]) D[i][j] = D[i][j-1]

 

/* DP. 배낭 */

w[i]: i번째 보석의 무게, v[i]:i번째 보석의 가치

D[i][j] : 0~i개까지의 보석을 사용하고, j무게 만큼의 제한이 있을 때, 취할 수 있는 최대 보석 가치

D[i][j] = (w[i] < j) ? max(D[i-1][j], D[i-1][j-w[i]] + v[i]) : D[i-1][j]

D[0][j]=0

 

/* DP. 롤러코스트 N개의 부품. L길이 롤러코스트. 총예산 B. 각 부품: Xi,Ci,Fi */

M[i][k]: i가 끝점인 곳에 위치한  k번째 부품 {x,f,c}

D[i][j]: i 길이 까지, j예산내에서 만들 수 있는 최대 재미값

 

D[i][j]=-1

newVal = D[M[i][k].x][j-M[i][k].c] + M[i][k].f

D[i][j] = (D[i][j]==-1 || D[i][j] < newVal) ? newVal

where

   j > M[i][k].c

  D[i-M[i][k].x][j-M[i][k].c] != -1

 

ans = max(D[L][i])

 

/* DP. 피보나치의 0,1 리턴 횟수 */

D[i][j] : 숫자 i에 대해서 j가 리턴되는 회수. 이 때 j=0 or 1

D[i][0] = D[i-1][0] + D[i-2][0]

D[i][1] = D[i-1][1] + D[i-2][1]

 

D[0][0] = 1, D[0][1] = 0

D[1][0] = 0, D[1][1] = 1;

 

 

/* DP. 이친수 0으로 시작하지 않고, 1이 연속 나타나지 않는 이진수 개수*/

D[i][0]: 0으로 끝나며, i자리인 이친수 개수

D[i][1]: 1으로 끝나며, i자리인 이친수 개수

 

D[i][0] = D[i-1][0] + D[i-1][1]

D[i][1] = D[i-1][0]

 

D[0][0] = D[0][1] = 0

D[1][0] = 0, D[1][1] =1

 

/* DP. 인접한 1인 비트의 수 */

D[i][j][0] : 0으로끝나면서, i개의 비트를 사용해서, j개의 인접비트수가 되는 수열 개수

D[i][j][1] : 1로끝나면서, i개의 비트를 사용해서, j개의 인접비트수가 되는 수열 개수

 

D[1][0][0] = D[1][0][1] = 1

 

2 <= i <= n,  0 <= j < i

D[i][j][0] = D[i-1][j][0] + D[i-1][j][1]

D[i][j][1] = D[i-1][j][0] + D[i-1][j-1][1]

 

/* DP. 동전1 n종류의 동전. 가치가가 K가 되도록하는 경우의 수 */

P[i]: i번째 동전의 가치

D[i][j] : 동전 i개를 써서, j원이 되게하는 경우의 수

D[i][j] = Sum(D[i-1][j-k*P[i]]), 0 <= k <= j/P[i]

D[0][0]=1

 

/* DP. 동전2 - n종류의 동전, K가 되도록하는 최소 동전갯수 */

p[i] : i번째 동전 가치. 1<=i<=n

D[i] : i원이 되는데 사용되는 최소동전갯수 (-1:impossible)  0<=i<=k

D[k]=?

D[i] = MIN(D[j] + D[i-j]), 0<=j<=(i/2) //D[3] = MIN(D[0]+D[3], D[1]+D[2])

       where D[j], D[i-j]가 존재할 때


[구현]

//1.초기화: 각 동전 한 종류로 만들 수 있는 금액 최솟값을 구해 놓는다. 

D[i] = -1, 1<=i<k 

tmp = p[i] * j; D[tmp] = (D[p[i] * j] == -1) ? j : min(D[tmp],j);


//2. 여러개 동전 조합으로 만들 수 있는 최솟값을 구한다.

D[0]=0

D[i] = (D[i]===-1) ? D[j] + D[i-j] : min(D[i], D[j] + D[i-j], 2<=i<=k, 1<=j<=(k/p[i])

 


/* DP. 포도주 시식 연속 3잔 마실 수 없음 */

- D[i] : i번째 포도주까지 최댓값

- 제일 마지막 포도주를 기준으로 해서, 아래 3가지 경우 중 최댓값.

  1)안마시는 경우  D[i-1]

  2)마시고, n-1 번째것을 마신 경우: V[i] + V[i-1] + D[i-3]

  3)마시고, n-1번째것을 안 마신 경우: V[i] + D[i-2]

 

cf)유사문제로 계단 문제가 있는데, 이 경우 제일 마지막 계단은 무조건 밟는 것으로, 포도주 문제와 약간 다름. 계단 처럼 무조건 마지막 계단을 밟는 경우는, 위의 2 3번 케이스만 고려하면 됨

 

/* DP. 막대기 n종류 막대기. 총길이가 k가 되도록하는 경우의 수 */

- n개가 아닌, n종류라는 데 주의. , 1종류를 여러번 사용 가능

- D[i][j]: i개까지의 막대기를 이용해서, j 길이 만큼을 만들 수 있는 경우의 수

- D[0][0]=1  ==> 0개의 막대기를 이용해서 길이 0만큼 만들 수 있는 경우의 수

  D[i][j] = Sum(D[i-1][j - k x s[i]), 0 <= k <= j/s[i]

  ==> i개까지 막대기를 사용해서 j길이만큼을 만들 수 있는 경우의 수는,

  한 개 덜 사용하면서(i-1개 사용하면서), i번째 막대기를 0~j/s[i]번까지 사용할 때의 경우의 수를 모두 합한 것.

  , i번째 막대기를 안 사용할 수도 있고, 1개 사용할 수도 2개 사용할 수도 있는 것.

  최대 몇개까지 사용할 수 있는 가는, j - k x s[i] > 0 이므로,  k <= j/s[i] 일 때까지.


/* DP. 합분해  0~N까지의 정수 K개를 더해 N이되는 경우의 수 */

- D[K][N]을 구하는 것. 숫자를 K개 더해서 그 합이 N이 되는 경우의 수

- D[i][j]: 숫자를 i개 더해서, 그 합이 j가 되는 경우의 수

- D[i][j] = D[i-1][j] + D[i-1][j-1] + ... +D[i-1][0]

           = Sum(D[i-1][j  k]), 0 <= k <= j

   ==> i개 더해서 합이 j가 되는 경우의 수는, i-1개 사용해서 j가 된 후 0을 더하거나(i번째 수로 0을 선택), i-1개 사용해서 합이 j-1인 상태에서 i번째로 1을 사용하거나,  j-2에서 2를 사용하는 등의 조합이 가능(수는 0~j까지 존재하니깐)

- D[0][0]=1 ==> 숫자 0개를 더해서 그 합이 0이되는 경우의 수는 1

 

/* DP. 돌다리 건너기  문자열 돌다리 2개를, 목표 문자열순이되게 번갈아가면서 밟는 경우수*/

An = 목표 문자열

Bm : 천사 돌다리

Cm : 악마 돌다리

D[i][j][0] : 천사돌다리로 끝나면서, i번째 목표문자열까지j번째 돌다리문자를 이용해서 만들 수 있는 최대 경우 수

D[i][j][1] : 악마돌다리로 끝나면서, ...

 

if(A[i] == B[j]) //목표문자열 i번째와 천사돌다리 j번째가 같다면

D[i][j][0] = Sum(D[i-1][k][1]), 0<= k <= j-1  // (j-1)까지의 악마돌다리에서의 경우의 수 합

if(A[i] == C[j]) //목표문자열 i번째와 악마돌다리 j번째가 같다면

D[i][j][1] = Sum(D[i-1][k][0]), 0<= k <= j-1  // (j-1)까지의 천사돌다리에서의 경우의 수 합

 

D[0][0][0] = D[0][0][1] = 1

  //0번째 목표문자열, 0번째 돌다리문자 이용 경우의 수로, 1의 의미가 좀 약하지만,

 //이렇게 해야 위의 점화식의 Sum값이 제대로 동작

 

ans = Sum(D[n][k][0] + D[n][k][1]), 1<= k <=m  //모두 더해줌에 유의. 달랑 D[n][m][0] + D[n][m][1] 이 아님

 //돌다리의 끝까지인 m까지 사용안하고도, 목표문자열을 만족시킬 수 있기 때문

 

/* DP. 마트료시카 - LIS */

- LIS(Longest Increasing Subsequence) 문제임. 즉, 최장 증가 수열을 찾는 것

- D[i]: 1~i번째 까지 인형을 사용해서 담을 수 있는 최대 인형 개수

- i번째 인형과, j=1~(i-1)번째 인형을 차례대로 비교. 그중에서 i번째 인형보다 작은인형들 중 가장 최대인 D[j]가 D[i]값이 됨

  for(int j=1;j<i;++j) if(s[i] > s[j]) D[i] = max(D[i],D[j])

기본으로 모든 D[i]값은, i번째 인형 자기 자신을 담을 수 있으므로 1이상. 즉, i번째 인형을 처리할 때 D[i]++

이렇게 1~N까지 구한 D[i]중에서 최댓값이 답

 for (i = 1; i <= n; i++){

        for (j = 1; j<i; j++){

               if (s[j] < s[i]) d[i] = max(d[i], d[j]);

        }

        d[i]++;

        ans = max(ans, d[i]);

}

 

/* DP. 폐지 줍기  N * N 격자에서 폐지 최대값 */

- D[i][j] : (i,j)칸에서의 폐지 최대값

  D[1][1] = a[1][1]

  D[i][j] = max(D[i][j-1], D[i-1][j]) + a[i][j]

==> a[i][j] += max(a[i][j-1], a[i-1][j] 로도 가능

 

/* DP. 조약돌 놓기  3 x n 테이블에 조약돌 최대값되게 놓기 */

- 1~i열까지의 최대합을 고려해보면, i열에 대해서, 다음과 같은 4가지 패턴으로 분류할 수 있다.

     i 열                              (i-1)열

  1)1행 값이 선택된 경우              max(패턴2, 패턴3) + 1행값

  2)2행                               max(패턴1, 패턴3) + 2행값

  3)3행                               max(패턴1, 패턴2) + 3행값

  4)1행과 3행                         패턴2 + 1행값 + 3행값

D[i][p] : i열이 패턴 p로 놓일 때의 최고 점수

  W[i][p] : i열이 패턴 p로 놓일 때, i열에 돌이 놓인곳의 점수 합 ->테이블값으로부터 미리 구해둔다.

- D[i][p] = max(D[i][q] + W[i][p] , i>=2 , q:p와 양립하는 패턴

  D[1][p] = W[1][p]

슈도코드

 for (i = 2 to n)

        for (p = 1 to 4)

               D[i][p] = max(D[i][q]) + W[i][p]

return max(D[n][p]), p:1~4

 

/* DP. 행렬곱 순서 */

D[i][j]: i번째 행렬에서부터 j번째 행렬까지의 최소 곱

D[i][j] = min(D[i][k] + D[k+1][j] + P[i-1] * P[k] * P[j]), i<= k <j

D[i][j] = 0 , when i == j

 

         for (int r = 1; r <= (N - 1); ++r) { //행렬을 몇개씩 묶으면서 시도해볼까를 나타냄. 1이면 두 개.

                  for (int i = 1; i <= (N - r); ++i) { //i번째 행렬부터,

                           int j = i + r;                    //j번째째 행렬까지

                           int low = INT_MAX;

                           int minK;

                           for (int k = i; k < j; ++k) { //k의 범위=[i,j)

                                   int tmp = D[i][k] + D[k + 1][j] + P[i - 1] * P[k] * P[j];

                                   if (tmp < low) {

                                            low = tmp;

                                            minK = k;

                                   }

                           }

                           D[i][j] = low;

                           T[i][j] = minK;   //괄호순서를 저장 

                  }

         }

 

//괄호 프린트하기

예를 들어 행렬 A1, A2, A3 가 있을 때, 계산결과 T테이블 값이 다음과 같이 도출되었다 하자.

T[1][2] = 1, T[2][3] = 2, T[1][3] = 2  나머지는 갱신안되어 zero

이것은 (A1A2)A3 로 계산되어야한다는 것을 의미. (if i==T[i][j] --> 그대로)

void print_array(int i, int j) {

        if (i == j) printf("A%d", i);

        else {

               printf("(");

               print_array(i, T[i][j]);

               print_array(T[i][j] + 1, j);

               printf(")");

        }

}

 

//실제 행렬곱

vector<vector<int> > doMulti(vector<vector<int> > a, vector<vector<int> > b) {

         int p, q, r;

         p = a.size();  q = b.size();  r = b[0].size(); 

         vector<vector<int> > c(p,vector<int>(r,0));

         for (int i = 0; i < p; ++i) {

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

                           c[i][j] = 0;

                           for (int k = 0; k < q; ++k) c[i][j] += a[i][k] * b[k][j];

                  }

         }

         return c;

}

 

vector<vector<int> > multi(int i, int j) {

         if (i == j) return v[i];

         return doMulti(multi(i,T[i][j]), multi(T[i][j]+1,j));

}


-끝-

반응형

'알고리즘 > 알고리즘 노트' 카테고리의 다른 글

[코드모음] - 문자열  (0) 2016.08.19
[코드모음]-정렬, 자료구조  (0) 2016.08.19
[코드모음]-기하,  (0) 2016.08.19
[코드 모음]-탐색, 그래프  (0) 2016.08.16
[수학]-문제  (0) 2016.08.11