蓝桥杯 摆动序列 O(1)解决

算法训练 摆动序列  
时间限制:1.0s   内存限制:512.0MB
      
锦囊1
状态压缩的动态规划。
问题描述
  如果一个序列满足下面的性质,我们就将它称为摆动序列:
  1. 序列中的所有数都是不大于k的正整数;
  2. 序列中至少有两个数。
  3. 序列中的数两两不相等;
  4. 如果第i – 1个数比第i – 2个数大,则第i个数比第i – 2个数小;如果第i – 1个数比第i – 2个数小,则第i个数比第i – 2个数大。
  比如,当k = 3时,有下面几个这样的序列:
  1 2
  1 3
  2 1
  2 1 3
  2 3
  2 3 1
  3 1
  3 2
  一共有8种,给定k,请求出满足上面要求的序列的个数。
输入格式
  输入包含了一个整数k。(k<=20)
输出格式
  输出一个整数,表示满足要求的序列个数。
样例输入
3
样例输出

8

#include <stdio.h>

int main()
{
    int n;
    scanf("%d", &n);
    printf("%d", (2<<n)-2-2*n);

    return 0;
}


表达不清的地方望见谅和指出



首先,很容易想到只考虑数列的最后两个  ..p,q
然后,可选数字被数列最后两个数分开成三段,L(<min{p,q}) / M(min< <max{p,q}) / R(max<)
数列最后两个数 先小后大 则选择 最大的一段 R 中的数 
数列最后两个数 先大后小 则选择 最小的一段 L 中的数 
发现无所谓是什么情况 都不会管中间段 M 中的数


其次,我们不在意具体是什么数字,而在意可以选择的数字有多少
如:
A: 数列:..5 9   剩余可用的数:3 4 / 6 7 8 / 10 13 
B: 数列:..4 10  剩余可用的数:1 3 / 7 8 9 / 13 14 
A,B数列最后两个数 都是先小后大
A,B剩余可用的数 都是 2个/3个/2个 
A选4 相当于 B选3 
A: 数列:..9  4  剩余可用的数:3 / 6 7 8 / 10 13 
B: 数列:..10 3  剩余可用的数:1 / 7 8 9 / 13 14 
A选13 相当于 B选14 
A: 数列:..4 13  剩余可用的数:3 / 6 7 8 13 / 
B: 数列:..3 14  剩余可用的数:1 / 7 8 9 14 / 
A选3 相当于 B选1
A: 数列:..13 3  剩余可用的数: / 6 7 8 13 / 
B: 数列:..14 1  剩余可用的数: / 7 8 9 14 / 
A,B数列最后两个数 总是同时保持 先小后大或先大后小 
A,B剩余可用的数 总是同时保持 相同的个数组合
可想而知,最后得出的结果(摆动序列个数,而不是具体的那些序列)也是相同的 


同样地,也不需要在意数列最后两个数具体是什么,只需要直到其大小顺序
 
于是,我们所需的状态的参数只有:数列最后两个数的大小顺序 mod 和 可选数字个数 a,c 
规定 mod=0: 先小后大 故需要选择更小的数 即在L中的a个数字中选择  
     mod=1: 先大后小 故需要选择更大的数 即在R中的c个数字中选择  


int f(int mod, int a, int c)








f(0, a, c):
mod=0 : 下一个数选择较小的数 
从a个数中选择一个,可能是第1个,第2个,...,第a个 
选择了第i个后,比Ai小的是第1,2,...,i-1个,比Ai小的是第i+1,i+2,...,a个 
那么之后的 a(可以选择的较小的数的个数)应为 i-1,更大的数不变
因此应该累加 f(1, i-1, c)
于是 f(0, a, c) = Sum{ For(i=0 To a-1) f(1, i, c) }
同理 f(1, a, c) = Sum{ For(i=0 To c-1) f(0, a, i) }


最终状态应是不能在递归的情况,即没有数可以选择,此时只有不添加数这一种情况:
故 f(0, 0, c)=1, f(1, a, 0)=1




于是得到最基础的状态转移方程: 
f(0, 0, c)=1
f(1, a, 0)=1 
f(0, a, c)= Sum{ For(i=0 To a-1) f(1, i, c) }
简写 F[0][a][c]=F[1][0 to a-1][c]
(?)(自)(如何想到) (原理实质)
?简写 也可当做 记录状态
f(1, a, c)= Sum{ For(i=0 To c-1) f(0, a, i) }
简写 F[1][a][c]=F[0][a][0 to c-1]


用这两个进行推导
f(0, a, c) =Sum{ For(i=0 To a-1) f(1, i, c) } 
=Sum{ For(j=0 To c-1) For(i=0 To a-1) f(0, i, j) } 
简写 F[0][a][c] =F[1][0 to a-1][c] =F[0][0 to a-1][0 to c-1]




继续推导 
F[0][a][c] =F[1][0 to a-1][c]
=F[1][0 to a-2][c]+F[1][a-1][c]
=F[0][a-1][c]+F[1][a-1][c]
F[1][a][c] =F[0][a][0 to c-1]
=F[0][a][0 to c-2]+F[0][a][c-1]
=F[1][a][c-1]+F[0][a][c-1]
于是  F[1][a-1][c] = F[0][a-1][c-1]+F[1][a-1][c-1] = F[0][a][c-1]


这样一来可以用 F[1][a-1][c]=F[0][a][c-1] 将 F[1] 全部装换为 F[0] 表示: 
F[0][a][c] =F[0][a-1][c]+F[1][a-1][c]
=F[0][a-1][c]+F[0][a][c-1]




列出一些结果后 
F[0]  0  1  2  3  4  5 
     __________________->c
  0 | 1  1  1  1  1  1    
  1 | 2  3  4  5  6     
  2 | 3  6 10 15       
  3 | 4 10 20            
  4 | 5 15         
  5 | 6                                                   
    |                
    ∨               
    a 
显然发现符合杨辉三角的特征 
 1  
 1  1  
 1  2  1  
 1  3  3  1   
 1  4  6  4  1     
 1  5 10 10  5  1      
 1  6 15 20 15  6  1    


F[0][a][c] = R(a+c+2)C(c+2)


证明:...




 
回到题目 
在 1-N 中选择两个数作为开头的两数 
并累加 每种开头的情况数 f(mod, a, c) 


(?)(自)(如何想到)(原理实质) 
改变思路 
改 For i=1 to N For j=i+1 to N f(0, i-1, N-j)+f(1, N-j, i-1) // ... 
为 (设定a+c=n则c=n-a) For n=0 to N-2 For a=0 to n f(0, a, c)+f(1, a, c) // ... 


ans =Sum{ For n=0 to N-2 For a=0 to n f(0, a, c)+f(1, a, c) } 
=Sum{ For n=0 to N-2 For a=0 to n f(0, a+1, c) } 




(?)(自)(如何想到)(原理实质) 
借助杨辉三角的有关思路:
列出一些结果后 
F[0]  0  1  2  3  4  5
     __________________->c 1  
  0 | 1  1  1  1  1  1     1  1  
  1 | 2  3  4  5  6     1  2  1 
  2 | 3  6 10 15       1  3  3  1   
  3 | 4 10 20             1  4  6  4  1 
  4 | 5 15         1  5 10 10  5  1 
  5 | 6                 1  6 15 20 15  6  1    
    |                
    ∨               F[0][a][c] = R(a+c+2)C(c+2)
    a




f(0, a+1, c) = R(a+1+c+2)C(c+2) = R(n+3)C(c+2)
于是 
ans =Sum{ For n=0 to N-2 For a=0 to n f(0, a+1, c) } 
=Sum{ For n=0 to N-2 For a=0 to n R(n+3)C(c+2) } 
=Sum{ For n=0 to N-2 For c=0 to n R(n+3)C(c+2) } (c=n-a) 
(?)(自)(如何想到)(原理实质) 
?用c=n-a 从For a=0 to n 直接得到For c=0 to n
=3到N+1行上的 第2到倒数第2个 数 的和 
=3到N+1行上的 全部数 的和 -行数*2(两边的数均为1)
杨辉三角上 第n行的数之和为2^(n-1): 
{
For m=1 to n R(n)C(m) 
=R(n)C(1)+ (R(n-1)C(1)+2*For m=2 to n-2 R(n-1)C(m)+R(n-1)C(n-1)) +R(n)C(n)
=2*R(n-1)C(1) + 2*For m=2 to n-2 R(n-1)C(m) + 2*R(n-1)C(n-1)
=2*(For m=1 to n-1 R(n-1)C(m))
=...=2*2*(For m=1 to n-2 R(n-2)C(m))
=...=2*2*2*(For m=1 to n-3 R(n-3)C(m))
=...
=...=2^(n-1)*(For m=1 to 1 R(1)C(m))
=2^(n-1)
}
于是
ans =3到N+1行上的 全部数 的和 -行数*2(两边的数均为1)
=2^2+2^3+2^4+...+2^n - 2*(N-1)
=(2^0+2^1+2^2+2^3+2^4+...+2^N)-(2^0+2^1)-2*(N-1)
=2^(N+1)-1-(2^0+2^1)-2*(N-1)
=2^(N+1)-4-2*N
=1<<(N+1)-2-2*N
=2<<N-2-2*N


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值