openjudge 9277 堆木头的解题报告。
DP-前缀和-优化。(重点)。
总时间限制: 1000ms 内存限制: 131072kB
描述
Daxinganling produces a lot of timber. Before loading onto trains, the timberjacks will place the logs to some place in the open air first. Looking from the sideway, the figure of a logs stack is as follows:
We have known that the number of logs in each layer is fewer than the lower layer for at least one log, and that in each layer the logs are connected in a line. In the figure above, there are 12 logs in the bottom layer of the stack. Now, given the number of logs in the bottom layer, the timberjacks want to know how many possible figures there may be.
给出在最底层的木头的个数,问有多少种堆放木头的方式,当然你的堆放方式不能让木头掉下来.
在堆放的时候木头必须互相挨着在一起.
输入
The first line of input contains the number of test cases T (1 <= T <= 1000000). Then T lines follow. Every line only contains a number n (1 <= n <= 200000) representing the number of logs in the bottom layer.
输出
For each test case in the input, you should output the corresponding number of possible figures. Because the number may be very large, just output the number mod 10^5.
样例输入
4
1
2
3
5
样例输出
1
2
5
34
提示
当输入3时,有5种方式
第一种:上面一个也不放
第二种:上面放一根,放在最左边
第三种:上面放一根,放在最右边
第四种:上面放二根
第五种:上面先放二根,然后在二根的上面放一根
这是一道很显然的动态规划的题,理解题意,这个题还是不是很刁难,因为所有的木头要求是必须连在一起,所以这个递推出来就很简单了,只需要考虑放的位置和组合的大小就没必要再考虑什么同一层有不同的组合。
like 你在最底层有5个的时候有4个空,而这个时候就不需要最左边的空考虑放什么,最右边的空放不放的问题。直接就是通过上一层的进行递推,而且,就只用考虑i-1层的摆放问题就好,组合就不用考虑。
举个栗子,这道题,底层为5,而作为倒数第二层就放1,那就4种方法,而如果就放2个,那那就有f[2]*3种方法,依次递推,这就是递推的伟大之处。
好了,献上状态转移方程。
嗯,还有标程。
1 #include<cstdio> 2 int f[200020]; 3 int main() 4 { 5 int t; 6 f[1]=1; 7 for(int i=2;i<=20;++i) 8 { 9 f[i]=1; 10 for(int j=1;j<=i-1;++j) 11 { 12 f[i]+=f[j]*(i-j); 13 } 14 } 15 scanf("%d",&t); 16 int a; 17 for(int i=1;i<=t;i++) 18 { 19 scanf("%d",&a); 20 int k=a; 21 printf("%d\n",f[a]); 22 } 23 return 0; 24 }
当然。如果你真的这么写。hhhhhh!!!当然不会过啊!肯定会超时啊!这样写就是个O(n^2)的算法。预处理就炸了。
所以。我们就得用点奇技淫巧。用动态前缀和来代替整个算法的过程。
而这个时候就得让我们更认真的研读这个状态转移方程(嗯,写的很装逼。)
f[i]=f[i-1]*1+f[i-2]*2+f[i-3]*3+.....+f[1]*(i-1)
f[i+1]=f[i]*1+f[i-1]*2+f[i-2]*3+.......+f[1]*i
发现他们的区别了吗!!发现了吗!!!没发现两式相减看看结果!嗯。每次发现都是多加了一组f[1]到f[i]。
好了!我们前缀和的方法,就是这个突破口。每次在动态维护数组f的时候,用一个动态的变量s来记录f[1]到f[i]的和。这样的话,就减去了很多重复的加法运算。
这种大数据量,就得认真的从优化入手。所有人都会想出状态转移方程,而问题。就是你。会不会优化了。
嗯。贴上程序。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int f[2000010]; 5 int main() 6 { 7 int n,t; 8 f[1]=1; 9 f[2]=2; 10 int s=1; 11 for(int i=3;i<=200000;++i) 12 { 13 s+=f[i-1]; 14 s%=100000; 15 f[i]+=f[i-1]+s; 16 f[i]%=100000; 17 } 18 scanf("%d",&t); 19 for(int i=1;i<=t;i++) 20 { 21 scanf("%d",&n); 22 printf("%d\n",f[n]); 23 } 24 return 0; 25 }