Time Limit: 1000MS | Memory Limit: 65536K | |||
Total Submissions: 23551 | Accepted: 6647 | Special Judge |
Description
Input
Output
Sample Input
([(]
Sample Output
()[()]
首先说,我对输方案目前阶段还有阴影,所以纠缠这道题很久……
不过仔细分析其实多简单的。
首先,分析如果不让输方案而是输添加括号的个数该怎么办:(觉得这个问题很弱的可以直接跳到空行的后面……顺便orz)
典型区间dp
f[i][j]表示区间i到j的串合法要添加的括号数
那么 转移方程:
1.如果c[i]和c[j]恰好是一对括号,那么: f[i][j] =min(f[i][j] , f[i + 1][j - 1])
2.否则 f[i][j] = min(f[i][j] ,f[i][k] + f[k + 1][j];
然后是要注意边界—— i==j 时 f[i][j] = 1;
接下来终于该考虑输出方案的问题了:
显而易见,只有我们知道了f[i][j]是从那里转移过来的我们就知道怎么加括号了(用g[i][j]记录)
如果是从第一种情况转移来那么应该是输出 :‘(’ + [i+1]到[j-1]的情况 + ‘)’
如果是从第二种情况转移来那么应该是输出 :i 到 g[i][j] 的情况 链接上 g[i][j]+1到r的情况这个显而易见是用递归很好实现的注意poj的读入有空行,所以用gets读入还要特判读入的是不是空字符串哦!!!
#include <iostream> #include <cstring> #include <string.h> #include <cstdio> using namespace std; const long N = 120; char c[120]; long f[120][120]; long g[120][120]; long n; void prt(long l, long r) { if (r == l) { if ((c[l] == '(') || (c[l] == ')')) printf("()"); if ((c[l] == '[') || (c[l] == ']')) printf("[]"); return ; } if (g[l][r] == -1) { if ( (c[l] == '(') && (c[r] == ')') ) { printf("("); prt(l + 1, r - 1); printf(")"); } if ( (c[l] == '[') && (c[r] == ']') ) { printf("["); prt(l + 1, r - 1); printf("]"); } } else{ prt(l, g[l][r]); prt(g[l][r] + 1, r); } } void dp() { n = strlen(c); memset(f, 127, sizeof(f)); memset(g, -1, sizeof(g)); for (long i = 0; i < n; i++) { f[i][i] = 1; } for (long l = 2; l <= n; l++) { for (long i =0; i + l - 1 < n; i++) { long j = i + l - 1; if (((c[i] == '[') && (c[j] == ']')) || ((c[i] == '(') && (c[j] == ')'))) { if (i + 1 == j) f[i][j] =0; if (f[i][j] > f[i + 1][j - 1]) { f[i][j] = f[i + 1][j - 1]; } } for (long k = i; k < j; k++) { if (f[i][j] > f[i][k] + f[k + 1][j]) { f[i][j] = f[i][k] + f[k + 1][j]; g[i][j] = k; } } } } //cout << f[0][n - 1] << endl; } int main() { freopen("poj1141.in", "r", stdin); while (gets(c)) { if (strlen(c) == 0) { printf("\n"); continue; } dp(); prt(0, n - 1); printf("\n"); } return 0; }