蓝桥 - 序列计数(记忆化搜索)
蓝桥 - 序列计数(记忆化搜索)
【问题描述】
小明想知道,满足以下条件的正整数序列的数量:
1. 第一项为 n;
2. 第二项不超过 n;
3. 从第三项开始,每一项小于前两项的差的绝对值。
请计算,对于给定的 n,有多少种满足条件的序列。
【输入格式】
输入一行包含一个整数 n。
【输出格式】
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
【样例输入】
4
【样例输出】
7
【样例说明】
以下是满足条件的序列:
4 1
4 1 1
4 1 2
4 2
4 2 1
4 3
4 4
【评测用例规模与约定】
对于 20% 的评测用例,1 <= n <= 5;
对于 50% 的评测用例,1 <= n <= 10;
对于 80% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000。
num(pre,cur) = num(cur,1) + num(cur,2) + ... +num(cur,abs(pre-cur)-1) + 1
pre表示前一个数,cur代表当前的数,选定之后,序列种数等于以cur为前序,以1到abs-1为当前的序列数的总和再加1.
如num(5,2) = num(2,1)+num(2,2).
优化一:
如果我们用num(i,j)表示前一个数是i,当前数是j的合法序列的个数;
比如求num(5,4),差为5-4=1,故它的结果等于num(3,4)
因此num(5,4)=num(3,4),求num(5,4)的时候可以先看num(3,4)求出来没,求num(3,4)的时候也可以先看num(5,4)求出来没。
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 const int INF=0x3f3f3f3f; const LL INFF=0x3f3f3f3f3f3f3f3f; 4 const double eps =1e-8; 5 const int mod=1e9+7; 6 const int maxn=1e5+10; 7 using namespace std; 8 9 int G[1005][1005];//记录状态 10 int n; 11 int ans; 12 13 void DFS(int fa, int u) 14 { 15 if(G[fa][u]!=-1) return ; 16 int cha=abs(fa-u); 17 if(fa>u&&u-cha>0&&G[u-cha][u]!=-1) 18 { 19 G[fa][u]=G[u-cha][u]; 20 return ; 21 } 22 else if(fa<u&&u+cha<=n&&G[u+cha][u]!=-1) 23 { 24 G[fa][u]=G[u+cha][u]; 25 return ; 26 } 27 G[fa][u]=1; 28 for(int i=cha-1;i>=1;i--) 29 { 30 DFS(u,i); 31 G[fa][u]=(G[fa][u]+G[u][i])%10000; 32 } 33 return ; 34 } 35 36 int main() 37 { 38 #ifdef DEBUG 39 freopen("sample.txt","r",stdin); 40 #endif 41 42 scanf("%d",&n); 43 memset(G,-1,sizeof(G)); 44 ans=0; 45 for(int i=n;i>=1;i--) 46 { 47 DFS(n,i); 48 ans=(ans+G[n][i])%10000; 49 } 50 printf("%d\n",ans%10000); 51 52 return 0; 53 }
优化二:
如果我们用nun(i,j)表示前一个数是i,当前数是1到j的合法序列的总个数;有num(i,j) = 1 + num(i,j-1) + num(j,abs(i-j)-1)
即分为两个部分:
(1)i作为前一个数,从1到j-1为当前数的合法序列的个数已经计算好
(2)求以j为尾数,后面选择1到abs(i-j)-1的合法序列的个数。
如 num(10,5)=num(10,4)+num(5,4);而不是枚举1到5;这样每次解答树只展开两个节点,相当于减少一层循环,虽然解答树的层次还是很深,但是由于记忆的存在,解空间仍然是N的平方。可在100ms内解决。
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 const int INF=0x3f3f3f3f; 4 const double eps =1e-8; 5 const int mod=1e9+7; 6 const int maxn=1e5+10; 7 using namespace std; 8 9 int G[1005][1005];//记录状态,用G(i,j)表示前一个数是i,当前数是1到j的合法序列的总个数 10 11 int DFS(int fa, int u) 12 { 13 if(u<=0) return 0; 14 if(G[fa][u]!=-1) return G[fa][u]; 15 return G[fa][u]=(1+DFS(fa,u-1)+DFS(u,abs(fa-u)-1))%10000; 16 } 17 18 int main() 19 { 20 #ifdef DEBUG 21 freopen("sample.txt","r",stdin); 22 #endif 23 24 int n; 25 scanf("%d",&n); 26 memset(G,-1,sizeof(G)); 27 printf("%d\n",DFS(n,n)%10000); 28 29 return 0; 30 }
-