13G:神奇的数列
Posted fangziyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了13G:神奇的数列相关的知识,希望对你有一定的参考价值。
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
一个正整数数列,可以将它切割成若干个数据段,每个数据段由值相同的相邻元素构成。该数列的神奇之处在于,每次切除一个数据段后,
该数据段前后的元素自动连接在一起成为邻居。例如从数列“2 8 9 7 7 6 9 4”中切除数据段“7 7 ”后,余下的元素会构成数列“2 8 9 6 9 4”
请问若要将该数列切割成若干个数据段,则至少会切出来几个数据段?样例: 按下列顺序切割数列“2 8 9 7 7 6 9 4”,只要切割成 6段
切割出“7 7”,余下 “2 8 9 6 9 4”
切割出 “6”,余下 “2 8 9 9 4”
切割出 “9 9”,余下 “2 8 4”
切割出 “2”,余下 “8 4”
切割出 “8”,余下 “4” - 输入
- 第一行是一个整数,示共有多少组测试数据。每组测试数据的输入包括两行:第一行是整数N, N<=200,表示数列的长度,第二行是N个正整数。
- 输出
- 每个测试案例的输出占一行,是一个整数。格式是:
Case n: x
n是测试数据组编号,x是答案 - 样例输入
-
2 8 2 8 9 7 7 6 9 4 16 2 8 9 7 7 6 9 4 4 2 8 4 2 7 6 9
- 样例输出
-
Case 1: 6 Case 2: 11
1 #include<iostream> 2 #include<queue> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 int a[202]; 7 int dp[202][202]; //从i到j需要的最小消除次数 8 int main(){ 9 int t; 10 cin>>t; 11 for(int tmp = 1; tmp <= t; tmp++){ 12 int n, i, len, k; 13 cin>>n; 14 memset(a, 0, sizeof(a)); 15 memset(dp,0,sizeof(dp)); 16 for(i = 1; i <= n; i++) 17 cin>>a[i]; 18 for(i = 1; i <= n; i++) 19 for(int j = i; j <= n; j++) 20 dp[i][j] = 1<<20; 21 for(i = 1; i <= n; i++){ 22 dp[i][i] = 1; 23 } 24 /* 25 for(i = 1; i < n; i++){ 26 if(a[i]==a[i+1]) dp[i][i+1] = 1; 27 else dp[i][i+1] = 2; 28 }*/ 29 for(len = 2; len <= n; len++){ 30 for(i = 1; i+len-1<= n; i++){ 31 int j = i+len-1; 32 for(k = i; k < j; k++){ 33 //2 8 9 7 9 6 9 34 if(a[k]==a[j]){ 35 dp[i][j] = min(dp[i][k]+dp[k+1][j-1], dp[i][j]); 36 } 37 else dp[i][j] = min(dp[i][k]+dp[k+1][j], dp[i][j]); 38 } 39 } 40 } 41 cout<<"Case "<<tmp<<": "<<dp[1][n]<<endl; 42 } 43 return 0; 44 }
备注:区间dp是有模板的,就像这样先枚举长度是常规操作。。
我真的不明白为什么这道题的动态转移方程为啥是这样。这都是咋想出来的啊。
x老师管这类题叫区间消消乐。我没完全看懂他写的那一整套套路,但我感觉就这道题好像可以解释了:
找a[k]==a[j]这个点是通用的套路,找到之后,先把(k+1,j-1)消掉,也就是后半段除了最后一个数,然后把a[j]留着和(i,k)一起消,这道题里没有什么乱七八糟的分值之类的设定,所以a[j]和前面(i,k)一起消的开销依然为dp[i][k]。所以动态转移方程是这样。
我觉得我看懂了。配合着blocks这道题理解就很容易了。
C. 删除数组
对于这种区间消消乐的题目,有2个套路:
1. 初始可以直接对原数组进行unique操作,即让所有重复段len=1
N = unique(s+1,s+1+N) - (s+1);
2. O(N^4) 区间DP
dp[l][r][len] = "消去[l,r]这段,以及l之前一段长度=len的且都=s[l]的相同元素"
a. len和s[l]直接一起消去:dp[l][r][len] = f(len+1) + dp[l+1][r][len], f()是1个和消去长度相关的函数
b. 找到(L,R]中某个s[i]==s[l],先消去[L+1,i-1]这段:dp[l][r][len] = dp[l+1][i-1][0] + dp[i][r][len+1]
这样可以应对大部分类似题目
本题中,f()恒等于1,所以unique之后可以直接去掉第三维,用dfs式DP解决,复杂度O(N^3)要求常数很小
int memo[MAXN][MAXN];//初始memo[i][i]=1,其他=0
int dfs(int l, int r){
if(memo[l][r]) return memo[l][r];
LL ans = 1 + dfs(l+1,r);
if(s[r]==s[l]) ans = min(ans, 1 + dfs(l+1,r-1));
for(int i=l+1;i<r;i++){
if(s[l]==s[i]) ans = min(ans, dfs(l+1,i-1) + dfs(i,r));
}
return memo[l][r] = ans;
}
Ans = dfs(1,N,0)
对于blocks这道题,就完美符合这个套路。
而对于我们这道简易版的题呢,len维度直接去掉就可以了,因为没有计分环节(能合并的话立即合并后消除总是最优的)。dp[l][r][len] = f(len+1) + dp[l+1][r][len]就是dp[l][r][len] = 1+ dp[l+1][r][len],和第二种情况可以合并,只要枚举k是从l到r-1就可以了。
好了!我明白了!
以上是关于13G:神奇的数列的主要内容,如果未能解决你的问题,请参考以下文章