구간 DP 요약

20457 단어 구간DP

구간DP는 주로 하나의 큰 구간을 몇 개의 단지 간으로 나누어 먼저 단지 간의 최우수치를 구한 다음에 합쳐서 큰 구간의 최우수치를 구한다.


구간 DP의 가장 관건적인 것은 최우수 서브구조와 후효성을 만족시키는 것이다!!!

//    DP    
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++) //     1    
    dp[i][i] = 0;
for (int len = 2; len <= n; len++) //      
{
    for (int i = 1, j = len; j <= n; i++, j++) //  [i,j]
    {
        //DP    
    }
}

우리는 dp를 상태 이동 방정식 수조로 규정하고 a는 데이터 수조(하표는 1부터),sum는 데이터 접두사와 수조로 a[]={1,2,3,4},sum[]={1,3,6,10}을 가설한다.

첫 번째 모델


돌병합


한 직선에 N더미의 돌이 있는데, 지금은 모든 돌을 한 무더기로 합쳐야 한다. 매번 서로 인접한 두 무더기만 합칠 수 있고, 합치는 비용은 새로 합성한 돌무더기의 수량으로 최소한의 비용을 요구한다.
1무더기, 비용은 02무더기, 비용은sum[2]3무더기, 비용은min(a[1]+a[2], a[2]+a[3])+sum[3]이다. 만약에 우리가 n무더기가 있다면 합병의 마지막 조작은 반드시 두 무더기에서 한 무더기로 합병한다.

첫 번째 모델은 큰 구간을 임의의 위치에서 두 개의 단지 사이로 나누는 것이다


규정 dp[i][j]가 제i더미에서 제j더미로 합병하기 위한 최소 비용 DP 방정식은 dp[i][j]=min(dp[i][k]+dp[k+1][j])+sum[j]-sum[i-1](i<=k// O(n^3) O(n^2) //http://blog.csdn.net/find_my_dream/article/details/4931222 memset(dp, 0x3f, sizeof(dp)); for (int i = 1; i <= n; i++) dp[i][i] = 0; for (int len = 2; len <= n; len++) { for (int i = 1, j = len; j <= n; i++, j++) { for (int k = i; k < j; k++) { if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]) dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]; } } } printf("%d
"
, dp[1][n]);

두 번째 모델


괄호 일치poj2955


괄호로 구성된 문자열에 최대 몇 개의 괄호([)]와 같은 문자열의 일치도는 2이지만 ()[], [()]와 같은 문자열의 일치도는 4이다. 즉, 괄호는 구분 작용을 한다.
길이 1의 직렬 일치도 0 길이 2의 직렬 일치도 0 또는 2
dp[i][j]가 i에서 j까지의 문자의 최대 일치도 길이를 n으로 합치기 위해 a[i]와 a[j]가 일치하는지 검사할 수 있습니다. dp[i][j]=dp[i+1][j-1]+2가 일치하지 않으면 우리는 첫 번째 모델에 따라 처리하고 임의의 위치에서 두 구간으로 나눌 수 있습니다.
while (gets(a+1))
{
    if (a[1] == 'e') break;
    memset(dp, 0, sizeof(dp));
    int n = strlen(a+1);
    for (int len = 2; len <= n; len++)
    {
        for(int i = 1, j = len; j <= n; i++, j++)
        {
            if((a[i]=='('&&a[j]==')') || (a[i]=='['&&a[j]==']'))
                dp[i][j] = dp[i+1][j-1] + 2;
            for (int k = i; k < j; k++)
                if(dp[i][j] < dp[i][k] + dp[k+1][j])
                    dp[i][j] = dp[i][k] + dp[k+1][j];
        }
    }
    printf("%d
"
,dp[1][n]); }

물론 이렇게 해서 두 번째 모델이라고 할 수는 없다
우리는 [i, j] 구간의 문자를 [i+1, j]가 앞에 문자를 붙이거나 [i, j-1]가 뒤에 문자를 붙이는 것으로 간주할 수 있다
여기서 우리는 [i, j]가 [i+1, j]에서 앞에 문자를 추가하는 상황만 고려한다. 만약에 a[i+1]에서 a[j]까지 a[i]와 일치하는 것이 없다면 dp[i][j]=dp[i+1][j]가 a[k]와 a[i]가 일치한다면 dp[k<=j]=max(dp[i][j], dp[i+1][k-1]+dp[k+1][j]+2).예를 들어: [xxxx] yyyyy는 괄호를 통해 두 개의 자열로 나뉜다

두 번째 모델은 일치하는 정보에 따라 구간을 [i+1,k-1]과 [k+1,j]로 나누는 것이다.

//        
while (gets(a+1))
{
    if(a[1] == 'e') break;
    memset(dp, 0, sizeof(dp));
    int n = strlen(a+1);
    for (int len = 2; len <= n; len++)
    {
        for(int i = 1, j = len; j <= n; i++, j++)
        {
            dp[i][j] = dp[i+1][j];
            for (int k = i; k <= j; k++)
                if((a[i]=='('&&a[k]==')') || (a[i]=='['&&a[k]==']'))
                    dp[i][j] = max(dp[i][j], dp[i+1][k-1] + dp[k+1][j] + 2);
        }
    }
    printf("%d
"
,dp[1][n]); }

두 문제 보고 이런 모형을 느껴보세요^-^

You Are the One HDU 4283


Halloween Costumes Light OJ-1422는 이와 별 차이가 없다. 한 무리의 깡패들이 한 줄로 서 있다. 첫 번째 K의 깡패 값은 a[i]*(k-1)이다. 작은 검은 집(창고)을 통해 순서를 조정할 수 있지만 먼저 방에 들어간 마지막에야 나올 수 있다. 깡패 값과 가장 작은 값을 구한다.사고방식: 우선, 우리는 창고에 순서가 있다는 것을 안다. 예를 들어 1, 2, 3이 모두 창고에 들어가면 반드시 3, 2, 1이 된다.dp[i][j]를 i개인부터 j개인까지 등장하는 최소 찌질치로 규정한다.만약에 i개인이 k번째로 출전(1<=k<=j-i+1)한다면 [i+1, i+k-1] 구간의 사람들은 반드시 i가 등장하기 전에 모두 출전해야 한다. 이때 마침 창고가 비어 있다. 앞의 k개인이 모두 출전한 다음에 i의 출전 순서로 구간을 구분할 수 있다??DP 방정식: dp[i][j]=min(dp[i][j], dp[i+1][i+k-1]+a[i]*(k-1)+dp[i+k][j]+(sum[j]-sum[i+k-1])*k)
memset(dp, 0, sizeof(dp)); //    dp[j+1][j]     ,   0   
for (int i = 1; i <= n; i++)
    for (int j = i+1; j <= n; j++)
        dp[i][j] = INF;
for (int len = 2; len <= n; len++)
{
    for (int i = 1, j = len; j <= n; i++, j++)
    {
        for (int k = 1; k <= j-i+1; k++)//k       [i,j],           
            dp[i][j] = min(dp[i][j], dp[i+1][i+k-1] + a[i]*(k-1) + dp[i+k][j] + (sum[j]-sum[i+k-1])*k);
    }
}

String painter HDU - 2476


이 문제는 먼저 구간 dp로 미리 처리해야 한다. 공백을 직렬 t로 바꾸는 최소 비용을 계산한 다음에 직렬 s에서 직렬 t로 가는 최소 비용을 계산한다. 코드를 보자.
#include
#include
#include
using namespace std;
const int MAXN = 105;
char s[MAXN],t[MAXN];
int dp[MAXN][MAXN];
int a[MAXN];
int main()
{
    while (~scanf(" %s %s", s+1,t+1))
    {
        int n = strlen(s+1);
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; i++) 
            dp[i][i] = 1;
        for (int len = 2; len <= n; len++)
        {
            for (int i = 1, j = len; j <= n; i++, j++)
            {
                dp[i][j] = 1 + dp[i+1][j]; //             
                for (int k = i+1; k <= j; k++)
                    if (t[i] == t[k])
                        dp[i][j] = min(dp[i][j], dp[i+1][k-1] + dp[k][j]);
            }
        }

        for (int i = 1; i <= n; i++)
        {
            a[i] = dp[1][i];
            if (s[i] == t[i]) 
                a[i] = a[i-1];
            else
            {
                for (int j = 1; j <= i; j++)
                    a[i] = min(a[i], a[j] + dp[j+1][i]);
            }
        }
        printf("%d
"
, a[n]); } return 0; }

세 번째 모델


이런 유형은 비교적 간단하기 때문에 매거구간 k∈[i,j]을 필요로 하지 않는다. 이런 유형은 좌우 경계와만 관련이 있다.

Cheapest Palindrome POJ 3280


제목: n개의 문자는 길이가 m인 문자열로 구성되어 있으며, 문자를 삭제하는 비용을 주고, 문자열의 임의의 위치에서 문자를 삭제할 수 있으며, 문자열을 회문열의 최소 비용으로 수정할 수 있습니다.dp[i][j]를 [i, j] 구간을 회문열로 바꾸는 최소 비용으로 회문열 [i+1, j]이 앞에 문자=>앞의 삭제 또는 뒤의 증가[i, j-1]를 뒤에 문자=>앞의 증가 또는 뒤의 삭제 등 네 가지 상황으로 볼 수 있다. a[i]=a[j]가 있을 때 dp[i+1][j-1]을 추가하면 된다.
#include
#include
#include
using namespace std;
const int MAXN = 2005;
char a[MAXN], ch;
int dp[MAXN][MAXN];
int add[30],sub[30];
int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    scanf("%s", a+1);
    for (int i = 1; i <= n; i++)
    {
        scanf(" %c", &ch);
        scanf("%d %d", &add[ch-'a'], &sub[ch-'a']);
    }
    memset(dp, 0x3f, sizeof(dp));
    for (int i = 1; i <= m; i++) 
        dp[i][i] = 0;
    for (int len = 2; len <= m; len++)
        for(int i = 1, j = len; j <= m; i++, j++)
        {
            dp[i][j] = min(dp[i][j], min(add[a[i]-'a'],sub[a[i]-'a']) + dp[i+1][j]);
            dp[i][j] = min(dp[i][j], dp[i][j-1] + min(add[a[j]-'a'],sub[a[j]-'a']));
            if (a[i] == a[j])
            {
                if (len==2) 
                    dp[i][j] = 0;
                else
                    dp[i][j] = min(dp[i][j], dp[i+1][j-1]);
            }
        }
    printf("%d
"
, dp[1][m]); return 0; }

Tian Ji – The Horse Racing HDU 1052


제목: 전기경마에서 한 경기를 이기면 200, 한 경기를 지고 200을 진다. 마지막에 가장 많이 이기면 얼마(or가 가장 적게 지는) 사고방식을 묻는다. 제왕이 가장 강한 말부터 시작한다고 가정하면 전기가 가장 강한 말이 제왕이 가장 강한 말을 이길 수 있다면 가장 강한 말로 이길 수 없고 가장 약한 말을 이길 수 있다.욕심 같지 않아요?그러나 가장 강한 타이브레이크를 처리하는 것은 비교적 번거롭기 때문에 타이브레이크를 선택하거나 지는 것을 선택할 수 있다.
예를 들어 전기의 말 속도는 2, 3으로 제왕 1, 3, 이렇게 속도는 3으로 비겼고 속도는 2로 이겼다.전기의 말 속도는 1, 2, 3, 4로 제왕의 말 속도는 1, 2, 3, 4로 스피드 1이 4에 졌고 다른 3승은 3승이었다.
하지만 매번 가장 강한 선수를 보내든지, 가장 약한 선수를 보내든지 분석할 수 있다.여기서는 DP 방법만 다룹니다.규정 dp[i][j]는 전기 제i에서 제j까지의 말과 제왕의 느린 j-i+1마리의 말의 승전보다 패배 수치를 감소시킨다
우리는 계속해서 이 예를 보았다. 전기의 말 속도 1, 2, 3, 4제왕의 말 속도 1, 2, 3, 4dp[1][3]는 전기의 속도 1, 2, 3의 말과 제왕의 속도 1, 2, 3의 말 비dp[2][4]는 전기의 속도 2, 3, 4의 말과 제왕의 속도 1, 2, 3의 말 비이다.
dp[1][4]는 전기의 속도가 1, 2, 3, 4인 말과 제왕의 속도가 1, 2, 3, 4인 말은 우리가 그를 [1,3]에 속도 4를 더한 말로 볼 수 있다. [2,4]에 속도 1을 더한 말은 제왕이 모두 속도 4를 더한 말이다.이때 두 가지 선택이 있다.가장 강한 말(속도 4)과 제왕의 강한 말을 겨루다.가장 느린 말(속도 1)과 제왕의 강마를
#include
#include
#include
#include
using namespace std;
const int MAXN = 1005;
int dp[MAXN][MAXN];
int tian[MAXN], qi[MAXN];
int val(int a, int b)
{
    if (a > b) return 1;
    if (a < b) return -1;
    return 0;
}
int main()
{
    int n;
    while (~scanf("%d", &n) && n)
    {
        for (int i = 1; i <= n; i++) scanf("%d", &tian[i]);
        for (int i = 1; i <= n; i++) scanf("%d", &qi[i]);
        sort(tian+1, tian+n+1);
        sort(qi+1, qi+n+1);
        memset(dp, 0x8f, sizeof(dp));
        for (int i = 1; i <= n; i++) 
            dp[i][i] = val(tian[i], qi[1]);
        for (int len = 2; len <= n; len++)
            for (int i = 1, j = len; j <= n; i++, j++)
                dp[i][j] = max(dp[i][j-1] + val(tian[j], qi[len]), dp[i+1][j] + val(tian[i], qi[len]));
        printf("%d
"
, dp[1][n] * 200); } return 0; }

좋은 웹페이지 즐겨찾기