HNU软件能力实训第二次模拟测试T2-冬城博物珍奇簿

【写在前面】

第一次写CSDN~谢谢各位点进来,如有意见建议欢迎提出~!*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

【原题呈现】

【问题描述】

帕姆听说贝洛伯格的历史文化博物馆重新开放了,非常好奇博物馆经营情况。

开拓者的经营水平飘忽不定,从第二天开始,后一天的经营额总是比前一天多 a 冬城盾或者少 b 冬城盾,第一天的营业额可以是任意整数。

开拓者经营了 n 天就被辞退了,总的经营额为 s。营业额可以为负。

帕姆想知道总共有多少种满足上述条件的可能的经营情况,你能帮帮它吗?

【输入形式】

输入的第一行包含四个整数 nsab,含义如前所述。

对于70%的数据,1≤n≤100,0≤s≤500,1≤ab≤50;

对于100%的数据,1≤n≤1000,-10^9≤s≤10^9,1≤ab≤10^6


【输出形式】

输出一行,包含一个整数,表示满足条件的方案数。

   由于这个数可能很大,请输出方案数除以100000007的余数。

【样例输入】

4 10 2 3

【样例输出】

2

【样例说明】

这两种经营可能情况分别是 2 4 1 3 和 7 4 1 -2。

【思路分析】

【初见】

第一眼看,这不应该dp嘛。定义 dp[i,j] 为在前 i 天,总经营额为 j 的方案数。

对于每一天,我们有两种选择:增加 a 冬城盾或减少 b 冬城盾。因此,状态转移方程为:

dp[i,j]=dp[i-1,j-a]+dp[i-1,j+b]

但是,我们很快发现,题目中的”初始经营额“是不固定的。这也就导致我们转移方程的原始状态是不确定的,这条思路很快就走不通了。

【再看】

题目虽然没有给出初始营业额,但告诉了我们最终营业额和每一天的营业额度,这在某种程度上好像在暗示我们将初始营业额表示出来。由于初始营业额一定是整数,我们似乎可以借助这条性质来判断其是否满足条件。

不管怎么说,Give a try!

列出以下方程:

nS_{begin}+(n-1)x_{1}+(n-2)x_{2}+...+x_{n-1}=S_{end}

其中,S_{begin},S_{end}分别为初始营业额,最终营业额;n为天数;x_{i}为第i天的营业额。

化简等式,即有

S_{begin}=\frac{S_{end}-\sum_{i=1}^{n-1}(n-i)x_{i}}{n},x_{i}\in \begin{Bmatrix} a,-b \end{Bmatrix}

由该式子我们可以得出:

  • 当x全部取a,对于给定的S,初始营业额最小;x取-b时初始营业额最大
  • 当我们将第i天的营业额从增加a替换为减少b时,对于初始营业额影响应为+(a+b)(n-i)

因此,我们只需要将初始状态设置成为初始营业额的最小值(即每天营业额都是增加a),然后枚举出所有天数组合的可能性方案,并判断当前获得的初始营业额是否为整数,此时问题就迎刃而解了!

【可能性计算】

由于天数最多有n-1天,而组合上限是\frac{n(n-1)}{2},对于每一个组合值,我们都需要找到从0~n-1中组合为该值的所有方案数,且要求不能重复选取。熟悉的味道来了——dp!

定义 dp[i,j] 为在前 i 天,组合总天数为 j 的方案数,得到状态转移方程:

dp[i,j]=dp[i-1,j]+dp[i-1,j-i]

将其优化成一维dp,注意j的反向:

 for (int i = 1; i <= n - 1; ++i) {
        for (int j = max_sum; j >= i; --j) {
            dp[j] += dp[j - i];
        }
    }

这样我们就得到了对于每个总天数t,在0~n-1天中不重复地选取组成t的所有方案数dp[t]。

【整数判定】

看到这里,先感谢您对本文章的支持~*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

这里我们先假设每天的营业额都比昨天多a,这样在最终营业额一定的情况下,此时的初始营业额一定是最少的,设为S_{low}。同时总天数设为\lambda\lambda \in [0,\frac{n(n-1)}{2}]

S_{begin}=\frac{S_{end}-\sum_{i=1}^{n-1}(n-i)x_{i}}{n},x_{i}\in \begin{Bmatrix} a,-b \end{Bmatrix}公式知,若使计算结果为整数,等式右侧分子部分应整除n。不妨设:

S_{low}=kn+f

同时对于每个天数的增加量a+b,有:

a+b=pn+q

那么就有:

\lambda (a+b)=\lambda pn+\lambda q

即:

S_{begin}=\frac{f+\lambda q}{n}+k+\lambda p

\lambda从0增大到\frac{n(n-1)}{2},只要满足这两项对n的余数为0,就能说明得出的初始营业额为整数,符合我们的要求,进而将此时的方案数统计下来,累加之,就是最终的答案。

代码实现如下:

int low = -sumn * a + s; 
	int cnt = 0;
    int f = low % n; 
    int q = a + b % n; 

    for (int i = 0; i <= max_sum; i++) {
        if ((i * q + f)% n == 0) {
            cnt += dp[i];
            cnt %= N;
        }
    }
return cnt;

【优化】

遗憾的是,目前代码只能通过4个样例,这说明我们的基本思路大致正确,但在某些方面仍然存在不足。🤔ing——

由于这道题数据量很大,我们可能对数据的处理还不够细致。考虑到cnt实质上是所有符合要求的dp结果之和,dp结果也有可能达到很大,同样需要取模运算。

而对于10^{9}的s,10^{3}的n和10^{6}的a,b;在我们的算法中有很大可能超出int表示的范围,选择把范围开到long long以保万无一失。

修改过后的代码如下:

    ll low = -max_sum * (ll)a + s; 
	// 使用 long long 类型进行计算
	int cnt = 0;
    ll f = low % n; 
    ll q = (a + b) % n; 

    for (int i = 0; i <= max_sum; i++) {
        if (((ll)i * q + f) % (ll) n == 0) {
            cnt += dp[i];
            cnt = cnt % N;
        }
    }

提交,全部AC~

【完整代码】

完整代码如下:

//Originated by Emanuel_CH in CSDN
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
typedef long long ll; // 使用 long long 类型以支持更大的数
const int N = 100000007;
ll dp[1000010];

int main() {
    int n, a, b;
    ll s; // 使用 long long 类型存储 s
    cin >> n >> s >> a >> b;
    int max_sum = n * (n - 1) / 2;

    dp[0] = 1;//初始状态为1

    for (int i = 1; i <= n - 1; ++i) {
        for (int j = max_sum; j >= i; --j) {
            dp[j] += dp[j - i];
            dp[j] %= N;
        }
    }

    ll low = -max_sum * (ll)a + s; 
	
	// 使用 long long 类型进行计算
	int cnt = 0;
    ll f = low % n; 
    ll q = (a + b) % n; 

    for (int i = 0; i <= max_sum; i++) {
        if (((ll)i * q + f) % (ll) n == 0) {
            cnt += dp[i];
            cnt = cnt % N;
        }
    }
    cout << cnt;
    return 0;
}

【时间复杂度】

  1. 第一个双层循环:外层循环从1到n-1,内层循环从max_sum到i,其中max_sum为n*(n-1)/2。因此,这部分的时间复杂度为O(n^2)。

  2. 第二个循环:从0到max_sum,时间复杂度为O(n^2),因为max_sum的最大值是n*(n-1)/2。

整个程序的时间复杂度主要由两个O(n^2)的循环决定,因此总的时间复杂度为O(n^2)。

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本次小班课的主题是计算机系统的存储器层次结构。 在计算机系统中,存储器是非常重要的组成部分,主要是用来存储程序和数据。存储器的层次结构是指不同速度和容量的存储设备按照一定的规律组织在一起,形成一个层次结构。 计算机系统中的存储器层次结构可以分为以下几层: 1. CPU内部寄存器:CPU内部寄存器是速度最快、容量最小的存储设备,一般用来存储CPU执行指令时需要的数据。 2. 高速缓存:高速缓存是一种速度较快、容量较小的存储设备,用来存储最常用的数据和指令,以便CPU能够快速地访问它们。 3. 主存储器:主存储器是计算机系统中容量最大、速度较快的存储设备,用来存储程序和数据。主存储器通常采用DRAM(动态随机存储器)芯片制成。 4. 辅助存储器:辅助存储器是一种容量较大、速度较慢的存储设备,主要用来存储程序和数据,例如硬盘、光盘、U盘等。 在计算机系统中,数据的存取速度与存储器的层次结构有关。如果数据在CPU内部寄存器或高速缓存中,则访问速度最快;如果数据在主存储器中,则访问速度较慢;如果数据在辅助存储器中,则访问速度最慢。 为了提高计算机系统的性能,可以采用以下策略: 1. 局部性原理:利用程序和数据的局部性原理,将最常用的数据和指令存放在高速缓存和主存储器中,以提高访问速度。 2. 缓存策略:采用合适的缓存策略,例如LRU(最近最少使用)策略、FIFO(先进先出)策略等,以尽可能地减少缓存的失效率。 3. 多级存储器:采用多级存储器的层次结构,以在速度和容量之间达到平衡,提高系统性能。 本次小班课的内容比较抽象,需要同学们认真理解。在实际应用中,存储器的层次结构也是非常重要的,可以直接影响计算机系统的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值