poj 1141 Brackets Sequence(DP第一弹)

poj   1141   Brackets Sequence                      题目链接:http://poj.org/problem?id=1141

                       

DP

题目大意:一个字符串只有方括号和圆括号四种字符,规定合法序列,要求将输入序列修改成最短合法序列输出。

合法序列定义:

    (1)空序列是一个合法的序列。

    (2)如果S是一个合法序列,那么(S)和[S]都是合法的序列

    (3)如果A和B都是合法序列,那么AB是合法序列。

题目分析:大部分注释在代码里,核心思想就是状态转移那部分,决策时涉及到二分。

code:

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
const int N=200;
string str;
int dp[N][N],path[N][N];
void oprint(int i,int j)
{
	if(i>j)return;//无效位置返回 
	if(i==j)//子列仅含一个字符 
	{//那就把它自己匹配起来 
		if(str[i]=='['||str[i]==']')printf("[]");
		else printf("()");
	}
	else if(path[i][j]==-1)//i、j可匹配 
	{
		printf("%c",str[i]);//先输左括号 
		oprint(i+1,j-1);//再输中间部分 
		printf("%c",str[j]);//后输右括号 
	}
	else
	{//其他情况,左右两部分分别输出 
		oprint(i,path[i][j]);
		oprint(path[i][j]+1,j);
	}
}
int main()
{
	while(getline(cin,str))
	{
		int n=str.size();
		if(n==0)
		{
			cout<<endl;
			continue;
		}
		memset(dp,0,sizeof(dp));
		for(int i=0;i<n;i++)
			dp[i][i]=1;//赋单括号的匹配数 
		for(int r=1;r<n;r++)//阶段:递推子序列的长度 
		{
			for(int i=0;i<n-r;i++)//状态:枚举子序列的开始位置 
			{
				int j=i+r;//根据起始位置、长度,算出结束位置 
				dp[i][j]=0x7fffffff;//状态转移方程初始化为无穷大 
				if((str[i]=='('&&str[j]==')')||(str[i]=='['&&str[j]==']'))
				//若当前子列的最外层括号已匹配,则可说明字列中括号亦已匹配(!!) 
				{
					dp[i][j]=dp[i+1][j-1];
					path[i][j]=-1;
					//continue;这句不能要啊,但是书上有!? 
				}
				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];
						path[i][j]=k;//记下最优中间位置 
					}
				}
			}
		}
		oprint(0,n-1);//具体输出 
		cout<<endl;
	}
	return 0;
}


PS:第一道稍难的DP,思路、代码完全照书来的,但是收获不小,收获主要来自书上的坑。这本书自称教材,上面居然有如此明显的逻辑错误,直接导致我WA多次,让我又一次清醒地认识到ACM王者之路上的坎坷与艰辛绝对不容小视。因祸得福,书上的这一错误,也让我对这个题的整体结构有了更进一步的认识。

PPS:还有一点,这个题poj上用string能过,用char数组就不行,奇怪~

最后老生常谈:DP,就是状态转移,就是多看一步,就是整体最优。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值