初学DP(1) 黑书中的《括号序列》

题目:括号序列

定义如下规则序列(字符串):

1.空序列是规则序列;

2.如果S是规则序列,那么(S)和[S]也是规则序列;

3.如果A和B都是规则序列,那么AB也是规则序列;

例如,下面的字符串都是规则序列:

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

而这几个就不是规则序列:

(,    [,    )(,    ([];

现在给出一些有‘(‘,‘)‘,‘[‘,‘]‘构成的序列,请添加少量的括号,得到一个规则的序列。

分析:

用s[i],s[j]表示字符串S的第i个字符和第j个字符,i<j,用ans[i][j]表示从第i个字符到第j个字符最少需要添加的括号数;

如果s[i]=‘(‘,s[j]=‘)‘或s[i]=‘[‘,s[j]=‘]‘,那么ans[i][j]=min(ans[i+1][j-1],ans[i][j]);

如果s[i]=‘(‘,s[j]!=‘)‘或s[i]=‘[‘,s[j]!=‘]‘,那么ans[i][j]=min(ans[i+1][j]+1,ans[i][j]);

如果s[i]!=‘(‘,s[j]=‘)‘或s[i]!=‘[‘,s[j]=‘]‘,那么ans[i][j]=min(ans[i][j-1]+1,ans[i][j]);

根据这个我们有如下递推关系:

int  bracket(int  i,int  j){
        if (i>j)    return 0;
        else if (i==j)    return 1;
        int answer = inf;
        if ((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))        answer =  min(answer,bracket(i+1,j-1));
        else if (s[i]=='(' || s[i]=='[')    answer = min(answer,bracket(i+1,j)+1);
        else if (s[j]==')' || s[j]==']')    answer = min(answer,bracket(i,j-1)+1);
        for (int k=i;k<j;k++)    answer = min(answer,bracket(i,k)+bracket(k+1,j));
        return answer;
}

我们发现上面的递归重复计算了好多,下面对于 bracket[0,3]进行说明

我们发现bracket[0,1]以及bracket[2,3]有重复操作,因此如果我们递归求bracket[0,len-1]中len超过100的话,重复操作非常大,很浪费时间;为此我们可以采取先计算出最低层的bracket,对于更高的bracket可以直接使用较低的bracket,这就是自底向下的递推;

参考代码:

#include <iostream>
#include <string.h>
using namespace std;
#define MAX 0xffffff
int main(){
 int dp[105][105];
 char s[105];
 while (cin>>s){
     int n=strlen(s);
     for (int i=0;i<n;i++){
         dp[i][i-1]=0;
         dp[i][i]=1;
     }
     for (int k=1;k<n;k++){	//表示i跟j之间的距离
          for (int i=0;i<n-k;i++){
              int j=i+k;
              dp[i][j]=MAX;
              if ((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']')){
                  dp[i][j]=dp[i+1][j-1];
              }
              for (int p=i;p<j;p++){
                  if (dp[i][j]>dp[i][p]+dp[p+1][j]){
                      dp[i][j]=dp[i][p]+dp[p+1][j];
                  }
             }
         }
      }
      cout<<dp[0][n-1]<<endl;
 }
 return 0;
}

poj 1141    http://poj.org/problem?id=1141

题目意思和上面一样,只是题目要你求出这个规则序列,为此我们只需要记录需要插入的位置,当我们需要输出时,采用递推的方式,对于从s[i]到s[j]的序列,如果pos[i][j]=-1则说明s[i]和s[j]括号匹配,我们可以先输出s[i],再继续考虑s[i+1]到s[j-1]的序列,最后输出s[j],如果pos[i][j]不等于-1则说明s[i]和s[j]不匹配,我们就需要对s[i]进行匹配,输出匹配后,我们再继续考虑s[i+1]到s[j]的序列;

#include <iostream>
#include <string.h>
using namespace std;
#define inf 0xffffff
int dp[105][105];
int pos[105][105];
char s[105];
void show(int i,int j){
	if (i>j)
		return;
	if (i==j){
		if(s[i]=='('||s[i]==')')
			cout<<"()";
  		else
  			cout<<"[]";
  	}
  	else{
  		if (pos[i][j]==-1){
  			cout<<s[i];
  			show(i+1,j-1);
  			cout<<s[j];
  		}
  		else{
  			show(i,pos[i][j]);
  			show(pos[i][j]+1,j);
  		}
  	}
}

int main(){
	cin>>s;
	int n=strlen(s);
	for (int i=0;i<n;i++){
		dp[i][i-1]=0;
		dp[i][i]=1;
	}
	for (int k=1;k<n;k++){	//表示i跟j之间的距离
		for (int i=0;i<n-k;i++){
			int j=i+k;
			dp[i][j]=inf;
			if ((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']')){
				pos[i][j]=-1;
				dp[i][j]=dp[i+1][j-1];
			}
			for (int p=i;p<j;p++){
				if (dp[i][j]>dp[i][p]+dp[p+1][j]){
					pos[i][j]=p;
					dp[i][j]=dp[i][p]+dp[p+1][j];
				}
			}
		}
	}
	show(0,n-1);
	cout<<endl;
	return 0;
}

注意:在poj上提交时如果用while(cin>>s)这个循环输入的话会使提交结果为WA,我也不知道为什么。。。

时间: 2024-10-20 06:57:30

初学DP(1) 黑书中的《括号序列》的相关文章

初学DP(2) 黑书中的《棋盘分割》

题意: 将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘.(每次切割都只能沿着棋盘格子的边进行) 原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和.现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小. 均方差,其中平均值,xi为第i块矩形棋盘的总分. 请编程对给出的棋盘及n,求出O'的最小值. 分析: 将公式化简可以得到σ2 = 1/n*

黑书例题 Fight Club 区间DP

题目可以在bnuoj.soj等OJ上找到. 题意: 不超过40个人站成一圈,只能和两边的人对战.给出任意两人对战的输赢,对于每一个人,输出是否可能是最后的胜者. 分析: 首先序列扩展成2倍,破环成链. dp[i][j]表示i和j能够相遇对打,那么dp[i][i+n]为真代表可以成为最后胜者. 枚举中间的k,若i和j都能和k相遇,且i和j至少一人能打赢k,那么i和j可以相遇. 复杂度o(n^3) 1 #include<cstdio> 2 #include<cstring> 3 usi

黑书练习题 更新中

1.4.7 奇数偶数 POJ 1733 Parity game 2.5.26 Unix 插头 POJ 1087 A Plug for UNIX 黑书练习题 更新中

黑书贪心例题之钓鱼 poj1042:Gone Fishing

总时间限制: 2000ms 内存限制: 65536kB 描述 John is going on a fishing trip. He has h hours available (1 <= h <= 16), and there are n lakes in the area (2 <= n <= 25) all reachable along a single, one-way road. John starts at lake 1, but he can finish at a

[ SHELL编程 ] shell中各种括号的使用方法

转载自:http://www.jb51.net/article/60326.htm 在这里我想说的是几种shell里的小括号,大括号结构和有括号的变量,命令的用法,如下:1.${var} 2.$(cmd) 3.()和{} 4.${var:-string},${var:+string},${var:=string},${var:?string} 5.$((exp)) 6.$(var%pattern),$(var%%pattern),$(var#pattern),$(var##pattern)现在分

数位dp(求1-n中数字1出现的个数)

题意:求1-n的n个数字中1出现的个数. 解法:数位dp,dp[pre][now][equa] 记录着第pre位为now,equa表示前边是否有降数字(即后边可不可以随意取,true为没降,true为已降):常规的记忆化搜索 代码: /****************************************************** * author:xiefubao *******************************************************/ #p

3295: 括号序列 -(序列DP)

描述 给定一串字符串,只由 “[”.“]” .“(”.“)”四个字符构成.现在让你尽量少的添加括号,得到一个规则的序列. 例如:“()”.“[]”.“(())”.“([])”.“()[]”.“()[()]”,都是规则的序列.这几个不是规则的,如:“(”.“[”.“]”.“)(”.“([()”. 输入 输入有多组测试数据.输入一串字符串序列,长度不大于255. 输出 输出最少添加的括号数目. 样例输入 () ( ([() [[(([] 样例输出 0 1 2 4 题目来源 椒江校区第一届C语言编程大

《九败一胜:美团创始人王兴创业十年》可以通过此书了解王兴和美团的一些事实。书中的概括评论部分我认为可以忽略 三星推荐

作者专门给企业和企业家写传,书中的内容还需要读者过滤.作者试图总结王兴的成功经验和王兴的优点,个人感觉这不是这本书最重要的,也不是作者擅长的.我最感兴趣的还是王兴和美团的历史和经验. 王兴在十年前创业初期做过十多个小项目,都是自己想出来的,都没做下去.后来做的主要是从美国复制:校内copy facebook(甚至copy了页面风格),饭否复制twitter,美团复制groupon.个人看法王兴最大的优势是多次的失败经验. 后面一半讲美团的发展历程.作者还跟着一些销售去签单子体验美团的流程.我本来

[ACM] hdu 3555 Bomb (数位DP,统计1-N中含有“49”的总数)

Bomb Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 7187 Accepted Submission(s): 2512 Problem Description The counter-terrorists found a time bomb in the dust. But this time the terrorists impro