• NEERC2002 Folding


    题目:Folding

    网址:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=0&problem=4505&mosmsg=Submission+received+with+ID+25160381

    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;
    }
    
  • 相关阅读:
    PHP 类的继承问题
    爬虫第一章
    如何给CBV添加装饰器
    结巴分词 gensim系数矩阵相似度 pypinyin
    elasticsearch 第二章 elasticsearch的详细用法及参数
    运维自动化 第五章 playbook 模块补充
    运维自动化 第四章 模块
    运维自动化 第三章 ansible
    正则补充
    运维自动化 第二章 openpyxl的用法,读写excel内容
  • 原文地址:https://www.cnblogs.com/zach20040914/p/13172161.html
Copyright © 2020-2023  润新知