“[”是开始操作A,“]”是结束操作A,“(”是开始操作B,“)“是结束操作B。定义这样的操作为合法操作序列。
1. 一个空的操作序列是合法操作序列
2. 先开始操作X(A 或 B),再进行一个合法操作序列,最后结束操作X,这样一个操作序列是一个合法操作序列
3. 把合法操作序列X和Y连起来,也是一个合法操作序列
如:
- (), [], (()), ()[()], (()()) 都是合法操作
- ([)], [[]), ( 都不是合法操作
GG有一本操作手册,在学习一个操作序列时,他发现这个序列可能不是一个合法序列,现在给出这个序列,请你回答最少需要往这个操作序列里面插入几次操作(插入“([])”中的一个字符算是插入一次操作),才能使这个操作序列变成合法操作序列。
Input
输入有多组数据,每组数据一行,只包括“([])”,长度不小于1,不超过100,至文件尾结束。保证数据数量小于等于15
Output
对每组数据,输出一行,即往这个操作序列里面最少插入的操作数目
这个操作真是可怕.
我竟然看了这道题一个小时,我都在想些什么啊….
状态表示:
dp[i][j]表示[i,j]内最少需要插入的操作数.dp[0,l−1]即为答案
d
p
[
i
]
[
j
]
表
示
[
i
,
j
]
内
最
少
需
要
插
入
的
操
作
数
.
d
p
[
0
,
l
−
1
]
即
为
答
案
边界情况:
dp[i][i]=1
d
p
[
i
]
[
i
]
=
1
状态转移:
dp[i][j]=minj−1k=i{dp[i][k]+dp[k+1][j]}
d
p
[
i
]
[
j
]
=
m
i
n
k
=
i
j
−
1
{
d
p
[
i
]
[
k
]
+
d
p
[
k
+
1
]
[
j
]
}
if(check(i,j))
i
f
(
c
h
e
c
k
(
i
,
j
)
)
dp[i][j]=min(dp[i][j],dp[i+1][j−1])
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
+
1
]
[
j
−
1
]
)
注意j=i+1的边界条件就好.
这道题是按长度规模递增递推的dp.
/* LittleFall : Hello! */
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read();
inline void write(int x);
const int M = 100016;
int dp[200][200]; //[起点,终点]
int equal(char a,char b)
{
return (a=='('&&b==')')||(a=='['&&b==']');
}
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
//std::cin.sync_with_stdio(false);
char p[200];
while(scanf("%s",p)!=EOF)
{
memset(dp,1,sizeof(dp));
int l=strlen(p);
for(int i=0;i<l;i++)
dp[i][i]=1;
for(int k=1;k<l;k++) //偏移量(长度-1)
for(int i=0;i<l-1;i++) //左边界,闭
{
int j=i+k;//右边界,闭
for(int m=i;m<j;m++) //划分位置
dp[i][j]=min(dp[i][j],dp[i][m]+dp[m+1][j]);
if(equal(p[i],p[j]))
dp[i][j]=min(dp[i][j],j-i>1?dp[i+1][j-1]:0);
}
printf("%d\n",dp[0][l-1] );
}
return 0;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}