解题思路 — C++

本文通过一个对话形式讲述了如何使用模拟小学乘法的方法来计算大整数乘积,如1000!,并解决了C++等编程语言中整型变量存储限制的问题。通过创建大数组存储每一位,逐位相乘并处理进位,实现了大数的计算。虽然这种方法的时间复杂度较高,但在给定的限制下(如1000!)不会超时。最后给出了代码实现作为参考。

萌新:这题我会,只要将1∼n 的每个数乘起来就好了。

大佬:那你算算 21!等于多少?

萌新: 我算算吼。21!=1×2×3×...×21 = 51090942171709440000

大佬: ??你怎么算的这么快?

萌新: 因为我用了计算器...

大佬: ......,那你发现什么了没有?

萌新: 51090942171709440000这个数太大了,C++的unsigned long long 最大值也才2^{64},存不下。

大佬: 是吧,所以你的想法是美好的,只可惜C++的整型变量根本...

萌新: 哦那我可以用 __int128

大佬: ......,__int128目前还有很多OJ不支持,就算支持,那 1000!1000! 你总没办法存了吧。

萌新: em,那该怎么办?

大佬: 别急,你先回想一下你小学老师教你的乘法是什么样的?

萌新: 小学学的乘法法大概是:相同数位对齐,然后选择其中一个数的各位数字乘以另一个数,并 把相乘的结果累加...

大佬: 是的,那么你就可以模拟这个操作:

  1. 定义一个大数组A[]来存大数,数组的一个元素存大数的一位。例如A[0]A[0]存个位,A[1]A[1]存十位,A[2]A[2]存百位,等等。
  2. 那么A[]A[]需要定义成多大?也就是说,1000!1000!有多少位?可以自己估计,不过,可以用 windows 自带的计算器能直接算出来,1000! ≈ 4×10^{2567}1000!≈4×102567。代码中定义一个更大的A[10000]。
  3. 模拟乘法计算,处理进位:例如356×8356×8。先计算个位的6×86×8,得4848,其中个位的88等于 48%10=848,进位的44等于48/10=448/10=4。见我代码中的10\sim 1310∼13行,这几行实际上是处理了两个数的乘法,请仔细分析这几行代码。
  4. 按3的计算方法,计算n!。第8行的i遍历了1∼n,计算n!。
  5. 最后打印是,从最高位开始打印。先找到最高位,即第一个不等于0的数,然后从高位往最低位打印。

好了,接下来请你自己编码完成这道题。下面的是我的代码,你可以作为参考。注意:代码第 33 行的 intA[] 是个大静态数组,应该定义在全局。如果定义在函数内部,有些编译器会出错哦。

萌新: 了解!不过大佬,这复杂度不会太高了吗?

大佬: 当然不会,你看我代码的第8行和10行,代码共循环计算了10000×n = 10000×1000 =10000×n=10000×1000= 1千万次,正好不会超时。这也是题目为什么要你计算1000!,若计算更大的数,用这个代码就可能超时了。

萌新: orz。

#include <bits/stdc++.h>
using namespace std;
int A[10000] = {0};  //存结果,注意大的静态数组要定义在全局
int main(){
    int n;
    cin >> n;

    A[0] = 1;
    for(int i = 1;i <= n;i++){
        int carry = 0;            //进位
        for(int j = 0;j < 10000;j++){
            A[j] = A[j] * i + carry;
            carry = A[j] / 10;
            A[j] = A[j] % 10;
        }
    }
    int last;
    for(int i = 10000 - 1;i >= 0;i--){
        if (A[i] != 0){
            last = i;
            break;
        }
    }
    for(int i = last; i >= 0;i--)
        cout << A[i];

    return 0;
}

### C++ 中解决“素数伴侣”问题的解题思路 #### 1. 题目分析 题目要求从给定的一组正整数中找出尽可能多的“素数伴侣”,即两个正整数之和为素数。为了实现这一目标,可以采用二分图匹配的方法来解决问题。 #### 2. 数据结构的选择 由于需要处理多个测试用例,并且每次都需要重新初始化数据结构,在C++中可以选择使用`vector<int>`作为动态数组存储奇数和偶数集合中的元素及其对应的索引关系。同时还需要定义一个布尔类型的访问标记数组用于记录当前节点是否已经被访问过,防止重复计算造成死循环。 #### 3. 构建二分图模型 对于每一个输入的数据集,将其按照数值分成奇数部分与偶数部分分别存入不同的向量容器内。接着通过双重for循环枚举每一对可能构成素数伴侣的关系并判断其合法性(即两者相加的结果是不是质数),如果是则在邻接矩阵中标记相应的边存在状态。 #### 4. 实现匈牙利算法求最大匹配数量 基于上述构建好的二分图框架,利用DFS深搜的方式模拟增广路径的过程尝试寻找更多的可行解直到无法继续扩展为止。具体来说就是在遍历过程中不断更新右侧顶点所关联左侧顶点的信息,当遇到未被占用的目标结点时立即终止递归返回成功标志;反之若所有候选者均已被其他左端点抢占,则回溯至上一层级重新考虑替代方案直至穷尽全部可能性。 ```cpp #include <iostream> #include <vector> using namespace std; bool isPrime(int n){ if (n <= 1) return false; for (int i = 2; i * i <= n; ++i) if (n % i == 0) return false; return true; } // 检查是否存在一条从u出发到达v的有效路径 bool dfs(const vector<vector<int>>& graph, int u, vector<bool>& visited, vector<int>& matchR) { for (auto v : graph[u]) { // 对于每个邻居节点v if (!visited[v]) { // 如果还没有访问过这个节点 visited[v] = true; // 将此节点设为已访问 // 若右边的点没有匹配 或 右边的点能够找到新的匹配 if (matchR[v] == -1 || dfs(graph, matchR[v], visited, matchR)) { matchR[v] = u; // 更新匹配关系 return true; } } } return false; } void solve() { int N; cin >> N; vector<int> nums(N); for(auto &num : nums) cin>> num; vector<int> oddNums, evenNums; for (const auto& num : nums) { if(num%2==0)evenNums.push_back(num);else oddNums.push_back(num); } const size_t m=oddNums.size(),n=evenNums.size(); vector<vector<int>>graph(m,vector<int>()); for(size_t i=0;i<m;++i){ for(size_t j=0;j<n;++j){ if(isPrime(oddNums[i]+evenNums[j])){ graph[i].push_back(j); } } } int result = 0; vector<int> matchR(n,-1); for (size_t u = 0; u < m; ++u) { vector<bool> visited(n,false); if(dfs(graph,u,visited,matchR)){ result++; } } cout <<result<< endl; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值