算法训练 摆动序列
时间限制: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
样例输出
表达不清的地方望见谅和指出
首先,很容易想到只考虑数列的最后两个 ..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
时间限制: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