PTA刷题

本文介绍了PTA平台上的三道算法题目,包括寻找正整数的最长连续因子个数、计算有理数序列的和以及解决功夫传人的功力计算问题。分别解析了解题思路并提供了关键代码片段。

1.连续因子

题目链接:这里
题意:一个正整数 N 的因子中可能存在若干连续的数字。例如 630 可以分解为 3×5×6×7,其中 5、6、7 就是 3 个连续的数字。给定任一正整数 N,要求编写程序求出最长连续因子的个数,并输出最小的连续因子序列。
思路:题意是要找到连续的最小的因子,并且这些因子的乘积任是这个数的因子,那么我们第一步要找到这个数的所有的因子,我们是通过for(int i = 2; i <= sqrt(n) + 1; i++)来找因子的,为什么要+1,虽然连续因子肯定不会以sqrt(n)+1打头,因为如果是素数,那只有一个因子即本身。如果不是素数,那么肯定有一个因子在小于等于sqrt(n),如果最长序列为1的话也是在2~sqrt(n)之中,不可能是sqrt(n)+1;如果最长序列大于1的话,更不可能是以sqrt(n)+1打头的,因为sqrt(n)+1乘以下一个因子必大于n。但sqrt(n)+1是有可能包括在前一个因子的序列中,就好比输入6的这种情况。所以sqrt(n)+1还是要考虑在内的,它后面的因子就不用考虑了,肯定是不可能的,因为不会有以它们开始的序列的。借鉴
看代码:

#include <bits/stdc++.h>
#define int long long

using namespace std;

signed main()
{
    int n; cin >> n;
    int m = sqrt(n);
    int s[m], t = 0;

    for (int i = 2; i <= m + 1; i++)
        if (n % i == 0) s[t ++] = i;

    if (t == 0) cout << '1' << endl << n  << endl;///素数的情况
    else if (t == 1) cout << '1' << endl << s[0] << endl;///只有一个因子的情况
    else
    {
        int len = 0, sum = 0, mlen = 0, pos = 0;
        for (int i = 0; i < t - 1; i ++)
        {
            sum = s[i], len = 1;
            for (int j = i; j < t - 1; j ++)
            {
                if (s[j + 1] - s[j] == 1 && n % (sum * s[j + 1]) == 0) {sum *= s[j + 1]; len ++;}
                else break;
            }
            if (len > mlen) {mlen = len; pos = i;}
        }

        cout << mlen << endl;
        cout << s[pos];
        for (int i = pos + 1; i < pos + mlen; i++)
            cout << '*' << s[i];
        cout << endl;
    }

    return 0;
}

2.N个数求和

链接
题意:是求N个数字的和。麻烦的是,这些数字是以有理数分子/分母的形式给出的,你输出的和也必须是有理数的形式。输出上述数字和的最简形式 —— 即将结果写成整数部分 分数部分,其中分数部分写成分子/分母,要求分子小于分母,且它们没有公因子。如果结果的整数部分为0,则只输出分数部分。
思路:这个题思路很简单,就是把分母通分,然后通分后把分子求出来,在约分,然后按照要求输出即可,最开始的时候我害怕爆longlong,在分子累加的时候就进行了约分,但是还是只有18分,后面才发现漏了一种情况,就是当你的结果类似于0/1这种分子是0,分母是其他任何非零数字的时候,这种情况下答案都是0。
AC代码:

#include <bits/stdc++.h>

#define int long long
#define x first
#define y second

using namespace std;

int n;
pair<int, int> q[110];

signed main()
{
    bool flag = true;
    cin >> n;
    for (int i = 1; i <= n; i++) scanf("%lld/%lld", &q[i].x, &q[i].y);

    int fm = q[1].y, fz = q[1].x;

    for (int i = 2; i <= n; i++)
    {
        int gcd = fm * q[i].y / __gcd(q[i].y, fm);
        fz = (fz * (gcd / fm) + q[i].x * (gcd / q[i].y));
        fm = gcd;
        int gcd_1 = __gcd(fz, fm);
        fz /= gcd_1, fm /= gcd_1;
    }

    int gcd = __gcd(fz, fm);
    fm /= gcd, fz /= gcd;

    int res_1 = fz / fm, res_2 = fz - res_1 * fm;

    if (res_1 == 0 && res_2 == 0) {puts("0"); return 0;}

    if (res_1 == 0) cout << res_2 << '/' << fm << endl;
    else if (res_2 == 0) cout << res_1 << endl;
    else cout << res_1 << ' ' << res_2 << '/' << fm << endl;

    return 0;
}

3.功夫传人

题意:假设家谱中的每个人只有1位师傅(除了祖师爷没有师傅);每位师傅可以带很多徒弟;并且假设辈分严格有序,即祖师爷这门武功的每个第i代传人只能在第i-1代传人中拜1个师傅。我们假设已知祖师爷的功力值为Z,每向下传承一代,就会减弱r%,除非某一代弟子得道。现给出师门谱系关系,要求你算出所有得道者的功力总值。
思路:我们可以用一个二维数组来表示某个节点的下一代,但是每个节点我们不知道到底有多少个下一代,那么我们可以用一个二维vector来存储,然后直接搜索就可以。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n;
double z, r, ans;
vector<int> chi[N];
double val[N];

void dfs(int id, double w)
{
    if (val[id]) ans = ans + w * val[id];
    else
    {
        for (int i = 0; i < chi[id].size(); i ++)
            dfs(chi[id][i], w * r);
    }
}

int main()
{
    memset(val, 0, sizeof val);
    ans = 0;
    scanf("%d %lf %lf", &n, &z, &r);
    r = (100 - r) / 100;

    for (int i = 0; i < n; i++)
    {
        int k; scanf("%d", &k);
        if (k == 0) scanf("%lf", &val[i]);
        else
        {
            for (int j = 0; j < k; j ++)
            {
                int x; scanf("%d", &x);
                chi[i].push_back(x);
            }
        }
    }

    dfs(0, z);

    printf("%d\n", (int)ans);

    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值