蓝桥杯 波动数列(01背包方案数)
来源:互联网 发布:南风知我意gl百度云 编辑:程序博客网 时间:2024/05/17 22:20
问题描述 观察这个数列:
1 3 0 2 -1 1 -2 ...
这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?输入格式 输入的第一行包含四个整数 n s a b,含义如前面说述。输出格式 输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。样例输入4 10 2 3样例输出2样例说明 这两个数列分别是2 4 1 3和7 4 1 -2。数据规模和约定 对于10%的数据,1<=n<=5,0<=s<=5,1<=a,b<=5;
对于30%的数据,1<=n<=30,0<=s<=30,1<=a,b<=30;
对于50%的数据,1<=n<=50,0<=s<=50,1<=a,b<=50;
对于70%的数据,1<=n<=100,0<=s<=500,1<=a, b<=50;
对于100%的数据,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。----------------------------------------------------------------------------------------------------------------------------------------------------
1 3 0 2 -1 1 -2 ...
这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
对于30%的数据,1<=n<=30,0<=s<=30,1<=a,b<=30;
对于50%的数据,1<=n<=50,0<=s<=50,1<=a,b<=50;
对于70%的数据,1<=n<=100,0<=s<=500,1<=a, b<=50;
对于100%的数据,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。
深度搜索算法:
这题较为容易的想法是用深度搜索来做,显然这种难度的题目用深搜是要超时的。果不其然,测试了一遍只能拿到20%。但是能在比赛最后一点时间拿到这些分也够了。而且,这题动态规划还是有点难度。
既然我测试过了深搜就一起先简单来看一下深搜的算法吧。这题的深搜就像一棵二叉树,从每个节点向下都会有两个分支,一个是加a,一个是减b。需要考虑的是根节点的大小,也就是第一个数的大小。第一个数没有明确告诉那么必然是在一个区间内浮动无法确定,需要一个个测试是否符合要求然后确定。注意到输入的数据有4个,第一个数的大小的区间可以用这四个数表示出来,可以确定最大值和最小值。最大值为s+n*b,最小值为s-n*a。对这个区间内的每个整数做一遍深搜,统计满足条件的值可得答案。
这部分比较好理解,看看代码:
#include<iostream>using namespace std;int n,s,a,b;int num;//cur是数列当前项的值,all是数列累计值的总和,id是数列的长度 void dfn(int cur,int all,int id){if(id ==n){if(all == s){num ++; //统计方案数 num = num % 100000007;return ;}else{return;}}dfn(cur+a,all+cur+a,id+1); //下一项加a的情况 dfn(cur-b,all+cur-b,id+1); //下一项减b的情况}int main(){long long i,total; cin >> n >> s >> a >> b;total = s+n*b;//第一个数可能取值的最大值 for(i=s-n*a;i<=total;i++){dfn(i,i,1);//对每个符合要求的数列的首项做一次深搜 }cout << num;return 0;}
动态规划算法:
简单介绍深搜后重点来了,这题用动态规划(dp)解才是正解。在讲这题之前首先要回顾一下01背包问题中统计方案数的问题。有容量为c的背包要装入体积为1~n的物品,每种物品各一个,求恰好将背包装满的方案数。
我们用二维数组Bo[i][j]来存储背包容量递增,物品按体积1~n的顺序递增时方案数的情况。i表示有体积为0~i的物品各一个,j表示背包的容量为j。首先要初始化当已有的物品体积数量为0,也就是相当于只有一个体积为0的物品时,背包容量j为0的方案数为1个,而容量大于0的方案数全为0。之后来看关键的状态转移方程:
Bo[i][j]=Bo[i-1][j] i>j
Bo[i][j]=Bo[i-1][j]+Bo[i-1][j-i] i<=j
二维数组是从上到下,从左到右进行计算的,每个元素都与之前已经计算过元素相关。第i行j列的元素,如果i>j,也就是新添加的可选物品的体积大于背包的容积,无法放入背包,所以新添加的可选物品不能够增加方案数。如果i<=j,新添加的可选物品的体积小于背包的容积,有可能与其他物品组合装入背包,这时方案数为当没有添加入i体积物品时背包容量为j的方案数Bo[i-1][j],加上当没有添加入i体积物品时但背包容量恰好可以装入i体积物品的方案数Bo[i][j]=Bo[i-1][j]+Bo[i-1][j-i]。这样逐行计算即可知道所有情况下的方案数。但是背包问题和本题有何关系呢,似乎毫不相干?
这题的难点就在于如何将本题转化为01背包问题。
设数列首项为t,F(i)={a,-b},第i项加上F(i)为第i+1项。所以第2项可表示为t+F(1),第2项可表示为t+F(1)+F(2),第3项可表示为t+F(1)+F(2)+F(3)...........把第一项到第n项累加起来可得表达式n*t + (n-1)F(1) + (n-2)F(2) + ....... +F(n-1) =s。n*t = s - {(n-1)F(1) + (n-2)F(2) + ....... +F(n-1)}。由于F(i)不为a就为-b,设有c个a,F(i)的系数相加共有1+2+......+n-1 = (n-1)*n/2项目,所以共有c-(n-1)*n/2个b。所以令temp = s - c*a + ((n-1)*n/2 - c)*b,当temp%n = = 0时,说明temp == n*t,满足要求。并且,c为1~n-1个数中的若干个数组合相加而得的,每一种组合都是一种方案,到此,我们就可以发现这题已经转化为求01背包问题的方案数了。即从体积为1~n-1的物品中有几种方案能够恰好填满背包。
还要注意到时间和空间的节省。时间上因为1~i-1体积的物品加起来最多也只能刚好填满i*(i+1)/2容量的背包,所以大于背包容量大于i*(i+1)/2体积的不用计算,固定初始化为0。空间上也不需要开辟上图那么大的空间,用滚动数组只需开辟两行即可,因为(i,j)的值只与i-1行有关,最终结果也只和最后求得的一行有关,所以只需存储当前行与上一行。
下面看看代码:
#include<iostream>using namespace std;#define MOD 100000007 #define MAXN 1100 long long n,s,a,b; long long all;long long Bo[2][MAXN*MAXN];//作为滚动数组 int p=0;//p为滚动数组标识,表示当前操作数组的第几行,(例如当前计算第i行,p指向操作Bo数组第0行,逻辑上i-1行是Bo数组第1行)void fun_dp(){long long i,j;//动态规划前初始化,只有一个体积为0的物品,可以装入容量为0的背包,容量大于0的背包方案数为0 Bo[p][0]=1;for(i=1;i<n;i++)//有体积为1到n-1的n-1种物品 {p=1-p;//p如果是0变换成1,如果是1变换成0 for(j=0;j<=i*(i+1)/2;j++)//背包容量从0到 i*(i-1)/2{if(i<j || i==j){Bo[p][j] = (Bo[1-p][j] + Bo[1-p][j-i]) % MOD;}else{ Bo[p][j] = Bo[1-p][j];} }}}int fun_sum(){long long count=0,i;long long temp;for(i=0;i<=all;i++){temp = s - i*a + (all - i)*b;if(temp%n == 0){count = (count+Bo[p][i])%MOD;}}return count;}int main(){long long count; cin >> n >> s >> a >> b;all = n*(n-1)/2; //最多可以增加多少个a(背包容量最大值) fun_dp();//进行动态规划的函数count = fun_sum();//统计总数cout << count;return 0;}最后还是有超时一点,拿了90%,还有10%束手无策了。我写的这篇应该也是我能找到的对于这题讲解最细的了,希望带来帮助。
再把10组测试数据送上
Intput : 5 0 1 2 output :4
15 14 12 7 1091
30 29 11 27 35791358
40 34 7 12 43894385
50 24 12 19 98280293
80 487 20 41 23283456
100 500 1 42 40778050
800 -34123 43141 134798 70420626
900 -931324123 987534 94273 41795042
1000 134182393 234891 410392 22371357
- 蓝桥杯 波动数列(01背包方案数)
- 蓝桥杯 波动数列 DP背包
- 蓝桥杯 历届试题 波动数列 DP 01背包 滚动数组
- 蓝桥杯---波动数列(dp)(背包)(待解决)
- 波动数列背包版.java
- 波动数列 - 蓝桥杯
- 蓝桥杯 波动数列 【未完成】
- 蓝桥杯:波动数列
- 蓝桥杯 波动数列
- 背包DP——波动数列
- 背包问题的方案数(01)
- 蓝桥杯 历届试题 波动数列
- 蓝桥杯之波动数列(未解决:运行超时)
- 蓝桥杯 波动数列 (DP&滚动数组 好题)
- 波动数列
- 波动数列
- 波动数列
- hdu2126(变形01背包,求方案数)
- Android音频焦点详解(上)
- 树莓派学习笔记一:ROS安装
- Swift3.0 反射
- ajax入门之建立XHR对象 (1)
- 【PAT】1051. Pop Sequence
- 蓝桥杯 波动数列(01背包方案数)
- 将比较大的镜像先上传到Ceph,再在glance中进行create
- opencv使用-HoughLinesP and HoughLines
- ZOJ - 3499 Median
- sql on hadoop方案(4)
- 用Java代码一键下载图片网站的全部图片
- Dubbo服务主机IP没有绑定的坑(dubbo注册时出现主机上没有的IP的解决方案)
- IntelliJ IDEA 控制台输出中文乱码问题的解决方法
- 【 USACO11JAN】 利润 【洛谷P3009】