NEERC2002 Folding
Posted zach20040914
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NEERC2002 Folding相关的知识,希望对你有一定的参考价值。
题目:Folding
Bill is trying to compactly represent sequences of capital alphabetic characters from ‘A’ to ‘Z’ by folding
repeating subsequences inside them. For example, one way to represent a sequence ‘AAAAAAAAAABABABCCD’
is ‘10(A)2(BA)B2(C)D’. He formally defines folded sequences of characters along with the unfolding
transformation for them in the following way:
- A sequence that contains a single character from ‘A’ to ‘Z’ is considered to be a folded sequence.
Unfolding of this sequence produces the same sequence of a single character itself. - If S and Q are folded sequences, then SQ is also a folded sequence. If S unfolds to S′ and Q unfolds to Q′, then SQ unfolds to S′Q′.
- If S is a folded sequence, then X(S) is also a folded sequence, where X is a decimal representation
of an integer number greater than 1. If S unfolds to S′, then X(S) unfolds to S′repeated X times.
According to this definition it is easy to unfold any given folded sequence. However, Bill is much
more interested in the reverse transformation. He wants to fold the given sequence in such a way that
the resulting folded sequence contains the least possible number of characters.
Input
Input file contains several test cases, one per line. Each of them contains a single line of characters
from ‘A’ to ‘Z’ with at least 1 and at most 100 characters.
Output
For each input case write a different output line. This must be a single line that contains the shortest
possible folded sequence that unfolds to the sequence that is given in the input file. If there are many
such sequences then write any one of them.
Sample Input
AAAAAAAAAABABABCCD
NEERCYESYESYESNEERCYESYESYES
Sample Output
9(A)3(AB)CCD
2(NEERC3(YES))
这道题是一道很好的区间DP问题。
大概是CSP-S第二天第一题水平。
显然,石子归并那样的题目要比LCS、0-1背包问题难,但是,基本上天底下的区间DP都长这个模样:
for 区间长度(阶段)
for 左结点(状态)
for 断点(决策)
转移
这道题也不例外,首先,不难定义状态:(dp[i, j])代表区间(dp[i, j]) 包括数字和括号最小长度。
我们慢慢来分析。
首先,对于([i, j]),可以转移到(dp[i, k])以及(dp[k + 1, j])两个字区间上。也就是讲,该区间最短长度是由(dp[i, k])以及(dp[k + 1, j])决定的;
其次,若该区间是由相同子序列构成,我们仍然可以直接将该序列转移至相同子序列中去。
那万一出现这类情况xxxyx,前三个可以合并,后面的单弄怎么办?实际上在第一步的过程中,已经考虑了这种情况,因为将整个序列扔给连续的子序列时,子序列的状态已经算过了(最优子结构吗);
所以最终的dp转移分两段:第一段将区间一分为二,第二段判断该区间是否可以被子序列缩进掉。
不过这道题在操作过程时还有几个点需要留心:
1.枚举子序列判断是否可以缩进时,应考虑区间长度的因数;
2.这道题特殊:字符串长度不超过100,因此缩进时不存在超过两位数的情况。也就是说,累加到答案的时候前面的数字最多最多为2。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 100 + 10;
char s[maxn];
bool book[maxn][maxn];//记录该区间是否直接缩进
int n, dp[maxn][maxn], f[maxn][maxn];//dp数组记录区间[i, j]最小长度 (包括数字与括号); f数组记录区间[i, j]是从哪儿转移过来的
bool valid(int L, int len, int p)
{
if(len % p) return false;
int i = 0;
while(i <= len - p)
{
for(int j = 0; j < p; ++ j)
if(s[L + j] != s[L + i + j]) return false;
i += p;
}
return true;
}
void search(int x, int y)
{
int k = f[x][y];
if(book[x][y]) search(x, k), search(k + 1, y);
else
{
int len = y - x + 1;
if(k == len)
for(int i = 0; i < k; ++ i)
printf("%c", s[i + x]);
else
{
int p = dp[x][x + k - 1];
printf("%d(", len / k);
search(x, x + k - 1);
printf(")");
}
}
return;
}
int main()
{
while(scanf("%s", (s + 1)) != EOF)
{
n = strlen(s + 1);
for(int l = 1; l <= n; ++ l)
{
for(int i = 1, j = l; j <= n; ++ i, ++ j)
{
int &ans = dp[i][j];
ans = l, book[i][j] = false, f[i][j] = l;
for(int k = i; k < j; ++ k)
{
if(ans > dp[i][k] + dp[k + 1][j])
{
ans = dp[i][k] + dp[k + 1][j], book[i][j] = true, f[i][j] = k;
}
}
for(int k = 1; k < l; ++ k)
{
if(valid(i, l, k))
{
//须要留意:被缩进的序列居然还可以嵌套
int t = dp[i][i + k - 1] + 2;
if(ans > t + (l / k >= 10 ? 2 : 1))
{
ans = t + (l / k >= 10 ? 2 : 1), book[i][j] = false, f[i][j] = k;
}
}
}
}
}
search(1, n);
puts("");
}
return 0;
}
以上是关于NEERC2002 Folding的主要内容,如果未能解决你的问题,请参考以下文章
Paper Folding UVA - 177 模拟+思路+找规律
Nova: Recursive Zero-Knowledge Arguments from Folding Schemes学习笔记