문제 해결: DP(정수 분할 문제)

8671 단어 DP단순 DP
총 시간 제한: 200ms 메모리 제한: 65536kB 설명은 정수 n을 일련의 정수의 합으로 나타낸다. n=n1+n2+...+nk, 그 중에서 n1>=n2>=>==nk>=1,k>=1.정수 n의 이런 표시를 정수 n의 구분이라고 한다.
입력 표준의 입력은 몇 개의 테스트 데이터를 포함한다.각 그룹의 테스트 데이터는 두 개의 정수 N과 K를 포함하는 한 줄의 입력 데이터이다.(0 < N <= 50, 0 < K <= N)
출력은 각 그룹의 테스트 데이터에 대해 다음과 같은 세 줄의 데이터를 출력한다. 첫 번째 줄: N은 K개의 정수의 합으로 나누는 두 번째 줄: N은 몇 개의 정수의 합으로 나누는 세 번째 줄: N은 몇 개의 서로 다른 정수의 합으로 나누는 네 번째 줄: N은 몇 개의 기정수의 합으로 나누는 세 번째 줄로 나뉜다.
샘플 입력 5 2
샘플 출력 2 7 3 3

분석하다.


n을 약간의 정정수의 합으로 나누는 구분수를 구하다
  • 만약에 여러 정수로 나누면 dp[i][j]를 같은 값으로 설정하여 i를 j보다 크지 않은 값으로 나눌 수 있다. (1) ij일 경우 구분에 j가 포함되어 있는지 여부에 따라 두 가지 상황으로 나눌 수 있다.만약 구분에 j가 포함된다면 구분 방안의 수는 dp[i-j][j]이다.만약에 획분수에 j가 없다면 i를 j-1보다 크지 않은 획수로 나누는 것과 같다. dp[i][j-1]이다.그래서 i>j일 때 dp[i][j]=dp[i-j][j]+dp[i][j-1];(3) i=j일 경우 구분에 j가 포함된 경우는 단 한 가지이며, 구분에 j가 없으면 i를 j-1보다 크지 않은 구분수로 나누는 것과 같다.이때 dp[i][j]=1+dp[i][j-1].dp[n][n]은 문제를 해결할 수 있다. 1,dp[n][k]는 n을 최대 수가 k를 초과하지 않는 구분수로 나누면 문제를 해결할 수 있다.
  • 만약에 구분된 정수는 반드시 서로 다른 dp[i][j]를 설정하여 i를 j를 초과하지 않는 서로 다른 정수로 구분해야 한다. (1) ij일 경우 구분에 j가 포함되어 있는지 여부에 따라 두 가지 상황으로 나눌 수 있다.만약에 구분에 j가 포함된다면 나머지 구분 중 가장 큰 것은 j-1이고 방안 수는 dp[i-j][j-1]이다.만약 구분에 j가 포함되지 않는다면 i를 j-1보다 크지 않은 구분수로 나누는 것과 같고 dp[i][j-1]이다.그래서 i>j일 때 dp[i][j]=dp[i-j][j-1]+dp[i][j-1];(3) i=j일 경우 구분에 j가 포함된 경우는 단 한 가지이며, 구분에 j가 없으면 i를 j-1보다 크지 않은 구분수로 나누는 것과 같다.이때 dp[i][j]=1+dp[i][j-1]
  • p[n][n]는 n을 서로 다른 정수로 나누는 구분수로 문제를 해결할 수 있음을 나타낸다.
    둘째, n을 k개의 정수로 나누는 구분수는 dp[i][j]를 j개의 정수로 나누는 구분수로 한다.(1) ij가 있다면 구분수에 1이 함유되어 있는지 여부에 따라 두 가지로 나눌 수 있다. 만약에 획분수에 1이 함유되어 있다면'절변법'을 사용하여 j개의 구분을 각각 1로 나누고 문제를 i-j의 j-1개의 구분수로 바꾸어 dp[i-j][j-1]로 전환할 수 있다.만약에 구분에 1이 포함되지 않는다면'절변법'을 사용하여 j개의 구분수의 맨 아래 수를 절단하고 제목을 i-j를 구하는 j개의 구분수로 바꾸어 dp[i-j][j]로 한다.그래서 i>j시 dp[i][j]=dp[i-j][j-1]+dp[i-j][j].
    p[n][k]는 n을 k개의 정수로 나누는 구분수로 문제를 해결할 수 있다.
    n을 약간의 정기수의 합으로 나누는 구분수
    f[i][j]를 j개의 홀수의 합으로 나누는 구분수로 설정하고, g[i][j]는 i를 j개의 짝수의 합으로 나누는 구분수로 한다.
    절단법을 사용하여 g[i][j]의 j개 구분을 모두 1로 나누면 f[i-j][j]를 얻을 수 있기 때문에
    g[i][j] = f[i-j][j].
    f[i][j]에는 1을 포함하는 구분 방안과 1을 포함하지 않는 구분 방안이 있다.
    1을 포함하는 구분 방안에 대해 1의 구분을 제외하고'i-1을 j-1의 홀수의 합으로 나누는 구분수', 즉 f[i-1][j-1]로 전환할 수 있다.1이 포함되지 않는 구분 방안에 대해 절단법으로 j개의 구분을 하나하나 1을 제거하고'i-j를 j개의 짝수의 합으로 나누는 구분수', 즉 g[i-j][j]로 전환할 수 있다.
    그래서 f[i][j]=f[i-1][j-1]+g[i-j][j].
    f[n][0]+f[n][1]+...+f[n][n]는 n을 약간의 홀수로 나누는 구분수로 문제 4의 답안이다.
    계속해서 코드에 주석이 있습니다
    #include
    #include
    #include
    #define maxn 55
    
    using namespace std;
    
    int num[maxn][maxn];//i   k      
    int num2[maxn][maxn];//              
    int f[maxn][maxn];//            
    int g[maxn][maxn];
    
    void init()
    {
        int i, j;
        for (int i = 1; i < maxn; i++)
            num[i][0] = num[0][i] = num2[i][0] = num2[0][i] = 0;
        for (int i = 1; i < maxn; i++)
        {
            for(int j = 1; j < maxn; j++)
            {
                if (i < j)
                    num[i][j] = 0;
                else if (i == j)
                    num[i][j] = 1;
                else//                  1          
                    num[i][j] = num[i - 1][j - 1] + num[i - j][j];
            }
        }
        f[0][0] = 1, g[0][0] = 1;
        for (i = 1; i < maxn; i++)
        {
            for (j = 1; j <= i; j++)
            {
                g[i][j] = f[i - j][j];
                f[i][j] = f[i - 1][j - 1] + g[i - j][j];
            }
        }
    }
    
    long long fun2(int n, int m)//   n    ,           m
    {
        if (n == 1 || m == 1)
            return 1;
        else if (n < m)
            return fun2(n, n);
        else if (n == m)
            return (1 + fun2(n, m - 1));
        else
            return (fun2(n, m - 1) + fun2(n - m, m));
    }
    
    int fun(int n, int m)//   n    ,          m
    {
        if (n == 1 && m == 1)
            return 1;
        else if (m == 1)
            return 0;
        else if (n < m)
            return fun(n, n);
        else if (n == m)
            return (1 + fun(n, m - 1));
        else
            return (fun(n, m - 1) + fun(n - m, m - 1));
    }
    
    int main(void)
    {
        init();
        int n, m, res;
        while (scanf("%d %d", &n, &m) != EOF)
        {
            printf("%d
    "
    , num[n][m]); printf("%d
    "
    , fun(n, n)); printf("%lld
    "
    , fun2(n, n)); res = 0; for (int i = 1; i <= n; i++) res += f[n][i]; printf("%d
    "
    , res); } return 0; }

    좋은 웹페이지 즐겨찾기