区间dp入门题目总结

石子合并

洛谷P1880
在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。php

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.ios

思路:
定义 dp[i][j] 为合并i到j堆石子所得的最大得分,用数组 sum[i] 记录1~i石子的石子数,经过 sum[j]sum[i1] 计算i~j石子的石子数,枚举变量k从i~j-1,状态转移方程为 dp[i][j]=max(dp[i][k]+dp[k+1][j]+sum[j]sum[i1]) ,求最小值相似。web

程序代码:算法

#include<iostream>
using namespace std;
int array[202];
int dp[202][202];
int dp2[202][202];
int sum[202];

int main()
{
    int i, j, k, l;
    int n;
    cin >> n;
    for(i=1; i<=n; i++)
    {
        cin >> array[i];
        array[n + i] = array[i];
    }
    for(i=1; i<=2*n; i++)
    {
        sum[i] = sum[i-1] + array[i];
    }

    for(l=2; l<=n; l++)     //枚举长度为l的区间 
    for(i=1; i+l-1<=2*n; i++)
    {
        j = i + l - 1;
        dp2[i][j] = 999999999;
        for(k=i; k<j; k++)
        {
            dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]);
            dp2[i][j] = min(dp2[i][j], dp2[i][k] + dp2[k+1][j] + sum[j] - sum[i-1]);
        }
    }

    int maxx = 0;
    int minn = 999999999;
    for(i=1; i<=n; i++)
    {
        if(maxx < dp[i][i+n-1])
        {
            maxx = dp[i][i+n-1];
        }
        if(minn > dp2[i][i+n-1])
        {
            minn = dp2[i][i+n-1];
        }
    }
    cout << minn << endl;
    cout << maxx << endl;

    return 0;
 }

Multiplication Puzzle

POJ 1651
The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row.数组

The goal is to take cards in such order as to minimize the total number of scored points.app

For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring
10150+50205+10505=500+5000+2500=8000 svg

If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be
15020+1205+1015=1000+100+50=1150. ui

题意:
n个数相乘,每次从中抽取一个数出来与相邻两个数相乘,直到抽到只剩两个数字,第一个数和最后一个数不能抽,求和最小值。atom

思路:
定义 dp[i][j] 为从i到j取数后值得最小值,枚举 kϵ(i,j) ,表示从i到j最后抽取第k个数字,
状态转移方程为 dp[i][j]=min(dp[i][k]+dp[k][j]+array[i]array[k]array[j]) spa

程序代码:

#include<iostream>
using namespace std;
int dp[101][101];
int array[101];

int main()
{
    int n;
    int i, j, k, l;
    cin >> n;
    for(i=1; i<=n; i++)
    {
        cin >> array[i];    
    }   

    for(l=3; l<=n; l++)
    for(i=1; i+l-1<=n; i++)
    {
        j = i+l-1;
        dp[i][j] = 99999999;
        for(k=i+1; k<j; k++)
        {
            dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + array[i] * array[k] * array[j]);
        }
    }

    cout << dp[1][n] << endl;

    return 0;
}

Brackets

POJ 2955
We give the following inductive definition of a “regular brackets” sequence:

the empty sequence is a regular brackets sequence,
if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
if a and b are regular brackets sequences, then ab is a regular brackets sequence.
no other sequence is a regular brackets sequence

For instance, all of the following character sequences are regular brackets sequences:

(), [], (()), ()[], ()[()]

while the following character sequences are not:

(, ], )(, ([)], ([(]

Given a brackets sequence of characters a1a2 … an, your goal is to find the length of the longest regular brackets sequence that is a subsequence of s. That is, you wish to find the largest m such that for indices i1, i2, …, im where 1 ≤ i1 < i2 < … < im ≤ n, ai1ai2 … aim is a regular brackets sequence.

Given the initial sequence ([([]])] , the longest regular brackets subsequence is [([])] .

题意:
求一字符串括号序列的最大匹配个数。

思路:
定义 dp[i][j] 为从i到j的最大匹配个数,若str[i]和str[j]相匹配,则 dp[i][j]=dp[i+1][j1]+2 ,枚举k为区间i~j的分段点,状态转移方程为 dp[i][j]=max(dp[i][k]dp[k+1][j])

程序代码:

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int dp[101][101];

int main()
{
    int i, j, k, l;
    string str;

    while(1)
    {
        cin >> str;
        memset(dp, 0, sizeof(dp));
        if(str == "end")
        {
            break;  
        }   

        for(l=2; l<=str.length(); l++)
        {
            for(i=0; i+l-1<str.length(); i++)
            {
                j = i+l-1;
                if((str[i] == '(' && str[j] == ')') || (str[i] == '[' && str[j] == ']'))
                {
                    dp[i][j] = dp[i+1][j-1] + 2;
                }

                for(k=i; k<j; k++)
                {
                    dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);
                }
            }
        }

        cout << dp[0][str.length()-1] << endl;
    }

    return 0;
}

Brackets Sequence

POJ 1141
Let us define a regular brackets sequence in the following way:

  1. Empty sequence is a regular sequence.
  2. If S is a regular sequence, then (S) and [S] are both regular sequences.
  3. If A and B are regular sequences, then AB is a regular sequence.

For example, all of the following sequences of characters are regular brackets sequences:

(), [], (()), ([]), ()[], ()[()]

And all of the following character sequences are not:

(, [, ), )(, ([)], ([(]

Some sequence of characters ‘(‘, ‘)’, ‘[‘, and ‘]’ is given. You are to find the shortest possible regular brackets sequence, that contains the given character sequence as a subsequence. Here, a string a1 a2 … an is called a subsequence of the string b1 b2 … bm, if there exist such indices 1 = i1 < i2 < … < in = m, that aj = bij for all 1 = j = n.

题意:
增长最少的括号使给定的括号序列匹配。

思路:
定义 dp[i][j] 为上题含义。
dp2[i][j] 表示i~j序列应从哪个位置切断,若i位置和j位置上括号匹配, dp2[i][j] = -1
枚举k位置,每次更新dp值时将 dp2[i][j]=k

采用递归打印
i=j ,打印”()”或”[]”
i<j ,根据 dp2[i][j] 的正负来递归
程序代码:

#include<iostream>
#include<string>
using namespace std;
string str;
int dp[101][101];
int dp2[101][101];

void print(int l, int r)
{
    if(l == r){
        if(str[l] == '(' || str[l] == ')'){
            cout << "()";
        }
        else if(str[l] == '[' || str[r] == ']'){
            cout << "[]";
        }
        return;
    }
    if(l > r){
        return;
    }

    if(dp2[l][r] < 0){
        cout << str[l];
        print(l+1, r-1);
        cout << str[r];
    }   

    else{
        int k = dp2[l][r];
        print(l, k);
        print(k+1, r);
    }
}

int main()
{
    int i, j, k, l;
    cin >> str;

    for(l=2; l<=str.length(); l++)
    for(i=0; i+l-1<str.length(); i++)
    {
        j = i+l-1;
        if((str[i] == '(' && str[j] == ')') || (str[i] == '[' && str[j] == ']'))
        {
            dp[i][j] = dp[i+1][j-1] + 2;
            dp2[i][j] = -1;
        }

        for(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];
                dp2[i][j] = k;
            }
        }
    }
    print(0, str.length()-1);
    cout << "\n";
    return 0;
}

You Are the One

hdu 4283
The TV shows such as You Are the One has been very popular. In order to meet the need of boys who are still single, TJUT hold the show itself. The show is hold in the Small hall, so it attract a lot of boys and girls. Now there are n boys enrolling in. At the beginning, the n boys stand in a row and go to the stage one by one. However, the director suddenly knows that very boy has a value of diaosi D, if the boy is k-th one go to the stage, the unhappiness of him will be (k-1)*D, because he has to wait for (k-1) people. Luckily, there is a dark room in the Small hall, so the director can put the boy into the dark room temporarily and let the boys behind his go to stage before him. For the dark room is very narrow, the boy who first get into dark room has to leave last. The director wants to change the order of boys by the dark room, so the summary of unhappiness will be least. Can you help him?

题意:
有一个队列,每一个人有一个愤怒值D,若是他是第K个上场,不开心指数就为(K-1)*D。可是边上有一个小黑屋(其实就是个堆栈),能够必定程度上调整上场程序,求愤怒值和最小值。

思路:
定义 dp[i][j] 为i~j序列愤怒和最小值
若第i我的第k个上场, kϵ[1,ji+1] ,状态转移方程为
dp[i][j]=min(array[i](k1)+dp[i+1][i+k1]+dp[i+k][j]+(sum[j]sum[i+k1])k)
其中 array[i](k1) 表示第i我的愤怒值, dp[i+1][i+k-1]表示前i-1我的愤怒值和,dp[i+k][j] + (sum[j] - sum[i+k-1]) * k表示第i我的以后人愤怒值和。

程序代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stdio.h>
using namespace std;
#define LL long long

LL dp[103][103];
LL array[103];
LL sum[103];

int main()
{
    //freopen("tt.in", "r", stdin);
    //freopen("xx1.out", "w", stdout);
    int t, n;
    int i, j, k, l, kk;
    cin >> t;

    for(kk=1; kk<=t; kk++)
    {
        cin >> n;
        memset(dp, 0, sizeof(dp));
        for(i=1; i<=n; i++)
        {
            cin >> array[i];
            sum[i] = sum[i-1] + array[i];
        }

        for(l=2; l<=n; l++)
        for(i=1; i+l-1<=n; i++)
        {
            j = i+l-1;
            dp[i][j] = (l-1)*array[i] + dp[i+1][j]; //设初始值 
            for(k=1; k<=l; k++)
            {
                dp[i][j] = min(dp[i][j], dp[i+1][i+k-1] + array[i] * (k-1) + 
                    dp[i+k][j] + (sum[j] - sum[i+k-1]) * k);
            }
        }

        cout << "Case #" << kk << ": " << dp[1][n] << endl;
    }   
}

String painter

hdu2476
There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?

题意:
求从A字串到B字串须要的最小步骤,每一步骤能够将一段字串变为同一字母字串

思路:
分两步。

  1. 定义dp[i][j] 为空字串到B字串须要的最小步骤,
    设初值 dp[i][j]=dp[i+1][j]+1 , 当前目标为i,枚举 kϵ[i+1,j] ,若 strB[i]=strB[k] ,表示可将i~k变为一种字母,以后处理 dp[i+1][k]
    状态转移方程为: dp[i][j]=min(dp[i+1][k]+dp[k+1][j])

  2. 定义dp2[i] 为A字串到B字串0~i序列的最小步骤,
    设初值 dp2[i]=dp[0][i] , 若 strA[i]=strB[j]dp2[i]=dp2[i1]
    状态转移方程: dp2[i]=min(dp2[j]+dp[j+1][i])

程序代码:

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int dp[102][102];
int dp2[102];

int main()
{
    string str1, str2;  
    int i, j, k, l;
    while(cin >> str1 >> str2)
    {
        memset(dp, 0, sizeof(dp));
        memset(dp2, 0, sizeof(dp2));

        for(i=0; i<str2.length(); i++){
            dp[i][i] = 1;
        }

        for(l=2; l<=str2.length(); l++)
        {
            for(i=0; i<str2.length(); i++)
            {
                j = i+l-1;
                dp[i][j] = dp[i+1][j] + 1;

                for(k=i+1; k<=j; k++)
                {
                    if(str2[i] == str2[k])
                    {
                        dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
                    }
                }
            }
        }

        for(i=0; i<str1.length(); i++)
        {
            dp2[i] = dp[0][i];
            if(i == 0){
                if(str1[i] == str2[i])
                {
                    dp2[i] = 0;
                }
                else{
                    dp2[i] = 1;
                }
            }   
            else {
                if(str1[i] == str2[i])
                {
                    dp2[i] = dp2[i-1];
                }
                else{
                    for(j=0; j<i; j++)
                    {
                        dp2[i] = min(dp2[i], dp2[j] + dp[j+1][i]);
                    }
                }
            } 
        }

        cout << dp2[str1.length()-1] << endl;
    }

    return 0;
 }