poj 1141(区间DP)

Brackets Sequence
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 29236   Accepted: 8319   Special Judge

Description

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.

Input

The input file contains at most 100 brackets (characters '(', ')', '[' and ']') that are situated on a single line without any other characters among them.

Output

Write to the output file a single line that contains some regular brackets sequence that has the minimal possible length and contains the given sequence as a subsequence.

Sample Input

([(]

Sample Output

()[()]

Source

 
区间DP的题,,就是不会打印路径,,写这道题的题解之前先说说《算法导论》的动态规划中的一道题吧。
《算法导论》矩阵链乘
题目:给定 n 个矩阵的链<A1,A2...An> 矩阵Ai 的规模为 pi-1*pi,给定完全括号化方案,使得A1A2..An所需标量乘法次数最少.
思路:能用动态规划的一个性质就是最优子结构性质,也就是说计算A[i:j]的最优次序所包含的计算矩阵
子链A[i..k]和A[k+1..j]的次序也是最优的。动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算.(更为详细可见算法导论)

状态转移方程: if(i==j) dp[i][j] = 0 ; if(i<j) dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+pi-1*pk*pj)
 
#include<string.h>
#include<stdio.h>
#include <iostream>
using namespace std;
#define N 50

int dp[N][N]; ///dp[i][j] 代表 AiAi+1...Aj相乘的最小代价;
int p[N];  /// Ai 矩阵的规模为 pi-1*pi ,两个相邻矩阵的相乘规模为 pi-1*pi*pi+1
int s[N][N]; ///记录 Ai - Aj中的最优分割点

void print_path(int i,int j){
    if(i==j) {
        printf("A%d",i);
    }else{
        printf("(");
        print_path(i,s[i][j]);
        print_path(s[i][j]+1,j);
        printf(")");
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<=n;i++){
        scanf("%d",&p[i]);
    }
    for(int i=1;i<=n;i++) dp[i][i]=0;
    for(int l=2;l<=n;l++){ ///枚举长度
        for(int i=1;i<=n-l+1;i++){ ///枚举起点
            int j = i+l-1;  ///终点
            dp[i][j]=0xfffffff;
            for(int k = i;k<j;k++){ ///枚举决策点k
                if(dp[i][j]>dp[i][k]+dp[k+1][j]+p[i-1]*p[k]*p[j]){
                    dp[i][j]=dp[i][k]+dp[k+1][j]+p[i-1]*p[k]*p[j];
                    s[i][j] = k;
                }
            }
        }
    }
    printf("%d\n",dp[1][n]);
    print_path(1,n);
}
///test: 6
///      30 35 15 5 10 20 25  结果为 15125次

了解了矩阵链乘,接下来这道题的思想就和矩阵链乘一样了。

状态转移方程: if(i==j) dp[i][j]=1;
                   if(i<j)  if(str[i]与str[j]匹配) dp[i][j] = dp[i+1][j-1]
                             else dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]);  k为断开的位置(决策点)

这题很坑,,用EOF结束死活过不了,看了一下Discuss区去掉EOF就AC

#include<string.h>
#include<stdio.h>
#include <iostream>
using namespace std;
#define N 105

char str[N];
int dp[N][N]; ///代表i-j区间最少添加的括号数
int s[N][N];
///当i>j时,直接返回,不需要输出
///当i==j时,s[i][j]为1,至少要加一个括号,如果str[i]为'(' 或者')',输出"()",否则输出"[]"
///当i<j时,如果s[i][j]>=0,说明从i到j断开了,则递归调用print_path(i, s[i][j])和print_path(s[i][j]+1, j);
///如果s[i][j]==-1,说明没有断开,如果 则输出str[i],print_path(i+1, j-1); 和str[j]

void print_path(int i,int j)
{
    if(i>j) return ;
    else if(i==j)
    {
        if(str[i]=='('||str[i]==')') printf("()");
        else printf("[]");
    }
    else
    {
        if(s[i][j]==-1)
        {
            printf("%c",str[i]);
            print_path(i+1,j-1);
            printf("%c",str[j]);
        }
        else
        {
            print_path(i,s[i][j]);
            print_path(s[i][j]+1,j);
        }
    }
}
int main()
{
    scanf("%s",str+1);   ///输入str+1 让字符串第0位为空,输入串占第 1-len位
    int len = strlen(str+1);
    for(int i=1; i<=len; i++) dp[i][i]=1;
    for(int l = 2; l<=len; l++)
    {
        for(int i=1; i<=len-l+1; i++)
        {
            int j = i+l-1;
            if((str[i]=='('&&str[j]==')')||(str[i]=='['&&str[j]==']'))
            {
                dp[i][j] = dp[i+1][j-1];
                s[i][j] = -1;
            }
            else dp[i][j]=0x7ffffff;
            for(int k=i; k<j; k++) ///寻找决策点 i<=k<j
            {
                if(dp[i][j]>dp[i][k]+dp[k+1][j])
                {
                    dp[i][j] = dp[i][k]+dp[k+1][j];
                    s[i][j] = k;
                }
            }
        }
    }
    //printf("%d\n",dp[1][len]);
    print_path(1,len);
    printf("\n");
    return 0;
}

 

posted @ 2016-04-10 17:18  樱花庄的龙之介大人  阅读(184)  评论(0编辑  收藏  举报