鱼C论坛

 找回密码
 立即注册
查看: 2437|回复: 22

求符合条件的正整数序列的数量

[复制链接]
发表于 2020-3-15 21:15:40 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 Given2001 于 2020-3-18 13:31 编辑

问题描述:

  小明想知道,满足以下条件的正整数序列的数量:

  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。

我的疑问
以下是我的代码,纯粹通过递归暴力算的,问题就是数据一大,甚至n超过15都要算半天
有没有更好的办法,或者优化我的方法(本人萌新,代码写的烂大佬勿怪)

  1. #include <iostream>
  2. #include <cmath>

  3. using namespace std;

  4. int n;
  5. int ret;
  6. void ca(int a, int b)
  7. {
  8.     int ab = abs(a - b);
  9.     int c;
  10.     for (c = 1; c < ab; c++)
  11.     {
  12.         ret++;
  13.         //cout << a << '\t' << b << '\t' << c << endl;
  14.         ca(b, c);
  15.         ret %= 10000;
  16.     }
  17. }
  18. int main()
  19. {
  20.     cin >> n;
  21.     ret = 0;
  22.     for (int i = 1; i <= n; i++)
  23.     {
  24.         ret++;
  25.         //cout << n << '\t' << i << endl;
  26.         ca(n, i);
  27.     }
  28.     cout << ret << endl;
  29.     return 0;
  30. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-3-16 15:41:35 | 显示全部楼层
研究了一晚上算是找到了点点规律,发出来大家共同学习=。=
(不一定对,有错误请大佬指正)
首先,我们先手算一下n为6时所有可能的序列(当然你也可以用代码)

  1. 6       1
  2. 6       1       1
  3. 6       1       2
  4. 6       1       3
  5. 6       1       3       1
  6. 6       1       3       1       1
  7. 6       1       4
  8. 6       1       4       1
  9. 6       1       4       1       1
  10. 6       1       4       1       2
  11. 6       1       4       2
  12. 6       1       4       2       1
  13. 6       2
  14. 6       2       1
  15. 6       2       2
  16. 6       2       3
  17. 6       3
  18. 6       3       1
  19. 6       3       1       1
  20. 6       3       2
  21. 6       4
  22. 6       4       1
  23. 6       4       1       1
  24. 6       4       1       2
  25. 6       5
  26. 6       6
复制代码

我们可以发现:
当 4 1 的后面分支都是相同的(我也表达不明白,看图)
可以得出:
当前一个数(a)和后一个数(b)相同时,产生的结果数也是相同的
于是我们将结果数保存在一个二维数组中,下一次要用时直接取用
经过测试,有效的减少了运算量,对于n=1000也能1s内得出结果
(虽然我也不知道答案是否准确就对了,希望有做出不同思路的大佬能跟我校对下答案)
贴下我优化后的代码
  1. #include <iostream>
  2. #include <cmath>
  3. //#include <ctime>

  4. using namespace std;

  5. int buff[1001][1001];
  6. //clock_t clSt, clEd;

  7. int ca(int a, int b)
  8. {
  9.     if (buff[a][b] != 0) {
  10.         //由于ab相同后续的可能性也相同,这里我直接取用之前保存的结果数
  11.         return buff[a][b];
  12.     }
  13.     int ab = abs(a - b);
  14.     int c;
  15.     int count = 1;
  16.     for (c = 1; c < ab; c++)
  17.     {
  18.         //cout << a << '\t' << b << '\t' << c << endl;
  19.         count += ca(b, c);
  20.     }
  21.     count %= 10000;//每次都对结果取余,防止溢出(可能。。。还是会溢出)
  22.     buff[a][b] = count;//保存结果
  23.     return count;
  24. }
  25. int main()
  26. {
  27.     int n;
  28.     cin >> n;
  29.     //初始化
  30.     //clSt = clock();
  31.     int ret = 0;
  32.     for (int i = 0; i < n; i++)
  33.     {
  34.         for (int j = 0; j < n; j++)
  35.         {
  36.             buff[i][j] = 0;
  37.         }
  38.     }
  39.     //第二个数
  40.     for (int i = 1; i <= n; i++)
  41.     {
  42.         //cout << n << '\t' << i << endl;
  43.         ret += ca(n, i);
  44.     }
  45.     ret %= 10000;
  46.     //clEd = clock();
  47.     cout << ret << endl;
  48.     //double runtime = (double)(clEd - clSt) / CLOCKS_PER_SEC;
  49.     //cout << "用时:" << runtime << endl;
  50.     return 0;
  51. }
复制代码

1

1

2

2

我的结果

我的结果
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-29 13:09:51 | 显示全部楼层
本帖最后由 Given2001 于 2020-3-29 13:19 编辑

2020年3月29日12:50:34 我又回来了
竞赛老师已经讲解了这道题,看到老师的代码,我……

话不多说,先上代码
  1. #include <cstdlib>
  2. #include <cstring>
  3. #include <ctime>
  4. #include <iostream>

  5. using namespace std;

  6. int N;
  7. const int MOD = 10000;
  8. int mem[1001][1000];

  9. int dfs(int pre, int cur) {
  10.     if (cur <= 0) return 0;
  11.     if (mem[pre][cur] != 0) return mem[pre][cur];
  12.     return mem[pre][cur] =
  13.                (1 + dfs(pre, cur - 1) + dfs(cur, abs(pre - cur) - 1)) % MOD;
  14. }

  15. void work() {
  16.     cin >> N;
  17.     cout << dfs(N, N) << endl;
  18. }

  19. int main() {
  20.     int a = clock();
  21.     work();
  22.     int b = clock();
  23.     clog << (b - a) << "ms" << endl;
  24.     return 0;
  25. }
复制代码


思路就是在我之前的优化代码上进一步优化。
原本递归函数中研究的是a与b所得出的结果数目
也就是说再最极端的情况下(记忆数组被填满)
我们需要运算结果1000*1000次,
再加上main函数里的一层循环
就是1000*1000*1000次,这时候我们就要考虑把最外层的循环给去掉
也就是把递归研究的问题转换为a与1~b所得出的结果总数
这样即使记忆数组被填满,也只有1000*1000次计算
大概是这样……

测试结果
14ms解决战斗!!
平均12ms就可以
原文地址

老师原文

老师原文
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-16 14:28:03 | 显示全部楼层
也不会 坐等大神回复
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-16 17:13:19 | 显示全部楼层

回帖奖励 +5 鱼币

一点都不萌新
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-16 23:07:48 | 显示全部楼层

回帖奖励 +5 鱼币

冒个泡
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 14:06:58 | 显示全部楼层

回帖奖励 +5 鱼币

跟大佬学习一下
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 14:39:00 | 显示全部楼层

回帖奖励 +5 鱼币

大佬,敬仰
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-17 15:16:46 | 显示全部楼层
大佬
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-3-17 15:37:23 | 显示全部楼层
没有大佬来验证下我的思路吗。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-17 15:46:34 | 显示全部楼层

我是货真价实的萌新啊,大佬可能根本不会考虑枚举把不懂大佬的世界
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-27 11:34:57 | 显示全部楼层

回帖奖励 +5 鱼币

完全看不懂,等大神指教
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-27 11:37:44 | 显示全部楼层

回帖奖励 +5 鱼币

枚举法行不。。。虽然很麻烦
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-27 14:14:30 | 显示全部楼层
乔珂珂 发表于 2020-3-27 11:37
枚举法行不。。。虽然很麻烦

我一开始的方法就是枚举啊,emmm你指的枚举是啥
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-27 14:53:32 | 显示全部楼层

回帖奖励 +5 鱼币

为什么数学界的小明也跑到编程界来了
纯属冒个泡,但这道题还真不会
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-28 12:20:34 | 显示全部楼层
烟肖雨晨 发表于 2020-3-27 14:53
为什么数学界的小明也跑到编程界来了
纯属冒个泡,但这道题还真不会

小明是个神秘的人物
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-29 08:23:51 | 显示全部楼层

回帖奖励 +5 鱼币

谢谢谢谢
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-29 10:43:31 | 显示全部楼层

回帖奖励 +5 鱼币

一点都不萌新
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-29 12:12:15 | 显示全部楼层
学习一下~
~~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-29 13:11:34 | 显示全部楼层

老师的标准答案出来了,有兴趣看看嘛
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-2 09:53

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表