【PAT】07 数学

第七章 数学

AcWing 1533. 1 的个数

问题描述

分析

  • 这一题最直观的做法,就是枚举1~n中的每个数,然后将每个数中1的个数加到res中,最后返回res。但是这种做法时间复杂度太高了。

  • 另一种做法是枚举每一位上1的个数,从高位开始枚举,假设我们的数据是abcdefg,如果我们现在枚举千位是1有多少种方案,需要分类讨论:

    (1)如果d==0,则d的左边可以取0~abc-1,右边可以取0~999,此时千位取1对应的数据都小于原数,一共 a b c × 1000 abc \times 1000 abc×1000种方案;

    (2)如果d==1,同样左边可以取0~abc-1,右边可以取0~999,一共 a b c × 1000 abc \times 1000 abc×1000种方案;另外左边可以取abc,右边可以取0~efg,对应 e f g + 1 efg+1 efg+1种方案,因此一共 a b c × 1000 + e f g + 1 abc \times 1000 + efg + 1 abc×1000+efg+1种方案。

    (3)如果d>1,左边可以取0~abc,右边可以取0~999,一共 ( a b c + 1 ) × 1000 (abc + 1) \times 1000 (abc+1)×1000种方案。

  • 下面的代码中用left记录上面演示的abc,用right记录上面演示的efg

代码

  • C++
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    
    int n;
    cin >> n;
    
    vector<int> nums;
    while (n) nums.push_back(n % 10), n /= 10;
    reverse(nums.begin(), nums.end());  // 为了让nums[0]对应最高位
    
    int res = 0;
    for (int i = 0; i < nums.size(); i++) {
        int d = nums[i];
        int left = 0, right = 0, p = 1;
        for (int j = 0; j < i; j++) left = left * 10 + nums[j];
        for (int j = i + 1; j < nums.size(); j++) {
            right = right * 10 + nums[j];
            p *= 10;
        }
        
        if (d == 0) res += left * p;
        else if (d == 1) res += left * p + right + 1;
        else res += (left + 1) * p;
    }
    
    cout << res << endl;
    
    return 0;
}

AcWing 1545. 质因子

问题描述

分析

  • 质因数分解,按照要求个数输出即可。

  • 关于质数的内容可以参考:质数

代码

  • C++
#include <iostream>

using namespace std;

int main() {
    
    int n;
    cin >> n;
    
    if (n == 1) puts("1=1");
    else {
        printf("%d=", n);
        bool is_first = true;
        for (int i = 2; i <= n / i; i++)
            if (n % i == 0) {
                int s = 0;
                while (n % i == 0) n /= i, s++;
                
                if (is_first) is_first = false;
                else printf("*");
                
                printf("%d", i);
                if (s > 1) printf("^%d", s);
            }
        if (n > 1) {
            if (!is_first) printf("*");
            printf("%d", n);
        }
    }
    
    return 0;
}

AcWing 1567. 有理数的和

问题描述

分析

  • 按照分数的加法进行计算即可,如下:

b a + d c = b c + a d a c \frac{b}{a} + \frac{d}{c} = \frac{bc+ad}{ac} ab+cd=acbc+ad

  • 计算的过程中要经常约分,这样计算才不会超出long long范围。上式计算的时候,要计算a、c的最小公倍数,不然最后一个数据无法通过。

代码

  • C++
#include <iostream>

using namespace std;

typedef long long LL;

LL gcd(LL a, LL b) {
    return b ? gcd(b, a % b) : a;
}

int main() {
    
    int n;
    scanf("%d", &n);
    
    LL b = 0, a = 1;  // b / a
    for (int i = 0; i < n; i++) {
        LL c, d;  // d / c
        scanf("%lld/%lld", &d, &c);
        
        LL t = gcd(c, d);
        c /= t, d /= t;
        
        t = gcd(a, c);
        b = c / t * b + a / t * d;
        a = a / t * c;
        
        t = gcd(a, b);
        a /= t, b /= t;
    }
    
    if (a == 1) printf("%d\n", b);
    else {
        if (b > a) printf("%d ", b / a), b %= a;
        printf("%lld/%lld\n", b, a);
    }
    
    return 0;
}

AcWing 1578. 有理数运算

问题描述

分析

  • 按照加减乘除的运算法则进行计算即可。如下:

b a + d c = b c + a d a c b a − d c = b c − a d a c b a ∗ d c = b d a c b a / d c = a d b c \frac{b}{a} + \frac{d}{c} = \frac{bc+ad}{ac} \\ \frac{b}{a} - \frac{d}{c} = \frac{bc-ad}{ac} \\ \frac{b}{a} * \frac{d}{c} = \frac{bd}{ac} \\ \frac{b}{a} / \frac{d}{c} = \frac{ad}{bc} ab+cd=acbc+adabcd=acbcadabcd=acbdab/cd=bcad

  • 为了防止溢出,使用long long存储数据。

代码

  • C++
#include <iostream>

using namespace std;

typedef long long LL;

LL gcd(LL a, LL b) {
    return b ? gcd(b, a % b) : a;
}

void print(LL a, LL b) {
    LL t = gcd(a, b);
    a /= t, b /= t;
    
    if (a < 0) a *= -1, b *= -1;
    bool is_minus = b < 0;
    
    if (is_minus) cout << '(';
    
    if (a == 1) cout << b;
    else {
        if (abs(b) > a) printf("%lld ", b / a), b = abs(b) % a;
        printf("%lld/%lld", b, a);
    }
    
    if (is_minus) cout << ')';
}

void add(LL a, LL b, LL c, LL d) {
    print(a, b), cout << " + ", print(c, d), cout << " = ";
    b = b * c + a * d;
    a *= c;
    print(a, b), cout << endl;
}

void sub(LL a, LL b, LL c, LL d) {
    print(a, b), cout << " - ", print(c, d), cout << " = ";
    b = b * c - a * d;
    a *= c;
    print(a, b), cout << endl;
}

void mul(LL a, LL b, LL c, LL d) {
    print(a, b), cout << " * ", print(c, d), cout << " = ";
    b *= d;
    a *= c;
    print(a, b), cout << endl;
}

void div(LL a, LL b, LL c, LL d) {
    print(a, b), cout << " / ", print(c, d), cout << " = ";
    if (!d) puts("Inf");
    else {
        b *= c;
        a *= d;
        print(a, b), cout << endl;
    }
    
}

int main() {
    
    // b/a ? d/c
    LL a, b, c, d;
    scanf("%lld/%lld %lld/%lld", &b, &a, &d, &c);
    
    add(a, b, c, d);
    sub(a, b, c, d);
    mul(a, b, c, d);
    div(a, b, c, d);
    
    return 0;
}

AcWing 1586. 连续因子

问题描述

分析

  • 我们枚举最长的连续因子的起点即可,然后找到以该点为起点的最长的一段,最后用这段更新答案即可。

代码

  • C++
#include <iostream>
#include <vector>

using namespace std;

int main() {
    
    int n;
    cin >> n;
    
    vector<int> res;
    for (int i = 2; i <= n / i; i++) 
        if (n % i == 0) {
            vector<int> seq;
            for (int j = i, s = n; s % j == 0; j++) {
                seq.push_back(j);
                s /= j;
            }
            if (seq.size() > res.size()) res = seq;
        }
    
    if (res.empty()) printf("1\n%d", n);
    else {
        cout << res.size() << endl;
        cout << res[0];
        for (int i = 1; i < res.size(); i++) cout << "*" << res[i];
    }
    cout << endl;
    
    return 0;
}

AcWing 1593. 整数分解

问题描述

分析

  • N、K看成背包的两个容量,对于每个数i 1 ≤ i ≤ 20 1 \le i \le 20 1i20,因为N最大为400)将其看成一个物品,其占用的两个容量分别为 i P 、 1 i^P、1 iP1,价值是i,求恰好将背包的两个容量装满获得的最大价值是多少?

  • 因此问题就转变为了一个二维费用背包问题(结合完全背包问题,因为每个物品可以使用多次)。关于背包问题请参考:背包问题(背包九讲)

  • 分析如下:

在这里插入图片描述

  • f(i, j, k)是上述所有选择的最大值,同样和完全背包一样,可以对状态转移进行优化:

  • f ( i , j , k ) = m a x ( f ( i − 1 , j , k ) , f ( i , j − i P , k − 1 ) + i ) f(i, j, k) = max(f(i-1, j, k), f(i, j - i^P, k-1) + i) f(i,j,k)=max(f(i1,j,k),f(i,jiP,k1)+i)

  • 对于输出方案,如果存在多种方案,我们需要输出字典序更大的方案,因为我们从小到大考虑每个物品的,因此最后直接反推得到方案即可。

代码

  • C++
#include <iostream>
#include <cstring>

using namespace std;

const int N = 410;

int n, K, p;
int f[21][N][N];

// 返回a^b
int pow(int a, int b) {
    int res = 1;
    for (int i = 0; i < b; i++) res *= a;
    return res;
}

int main() {
    
    cin >> n >> K >> p;
    
    memset(f, -0x3f, sizeof f);
    f[0][0][0] = 0;
    
    int m = 0;  // 记录考虑到的物品数
    for (int i = 1; ; i++) {
        int v = pow(i, p);  // 物品的体积
        if (v > n) {
            m = i - 1;
            break;
        }
        
        for (int j = 0; j <= n; j++)
            for (int k = 0; k <= K; k++) {
                f[i][j][k] = f[i - 1][j][k];
                if (j >= v && k) f[i][j][k] = max(f[i][j][k], f[i][j - v][k - 1] + i);
            }
    }
    
    if (f[m][n][K] < 0) puts("Impossible");
    else {
        printf("%d = ", n);
        bool is_first = true;
        while (m) {
            int v = pow(m, p);
            while (n >= v && K && f[m][n - v][K - 1] + m == f[m][n][K]) {
                if (is_first) is_first = false;
                else printf(" + ");
                
                printf("%d^%d", m, p);
                n -= v, K -= 1;
            }
            m--;
        }
    }
    
    return 0;
}

AcWing 1594. 数段之和

问题描述

分析

  • 考虑每个数据会在多少个区间出现,假设有n个数据,则考虑第i个数据在多少个区间出现:以第i个数据为结尾的区间有i个,以第i个数据为起点的区间有n - i + 1个,因此根据乘法原理,第i个数据一共会在i*(n-i+1)个区间出现。

  • 注意,为了达到精度,这里需要使用long double

代码

  • C++
#include <iostream>

using namespace std;

int main() {
    
    int n;
    cin >> n;
    long double res = 0;
    for (int i = 1; i <= n; i++) {
        double x;
        cin >> x;
        
        res += x * i * (n - i + 1);
    }
    
    printf("%.2Lf\n", res);
    
    return 0;
}

AcWing 1602. 卡住的键盘

问题描述

分析

  • 使用st数组记录哪些字符是正常的,如果一个字符是正常的,则记为1,不正常的记为0

  • 最后根据st数组进行输出。

代码

  • C++
#include <iostream>

using namespace std;

const int N = 300;

// 每个字符的状态
// 0: 可能损坏的按键,但未输出; 1: 正常按键; 2: 可能损坏的按键,并且已经输出
int st[N];

int main() {
    
    int k;
    string s;
    cin >> k >> s;
    for (int i = 0; i < s.size(); i++) {
        int j = i + 1;
        while (j < s.size() && s[j] == s[i]) j++;
        
        int cnt = j - i;
        if (cnt % k) st[s[i]] = 1;
        i = j - 1;
    }
    
    string res;
    for (int i = 0; i < s.size(); i++) {
        if (st[s[i]] == 0) cout << s[i], st[s[i]] = 2;
        
        if (st[s[i]] == 1) res += s[i];
        else {
            res += s[i];
            i += k - 1;
        }
    }
    cout << endl << res << endl;
    
    return 0;
}

AcWing 1606. C 语言竞赛

问题描述

分析

  • 本题相当于让我们判断一个数是质数还是合数。可以使用埃式筛法求解。

  • 关于质数内容可以参考:质数

代码

  • C++
#include <iostream>

using namespace std;

const int N = 10010;

int n;
int rnk[N];  // 每个人对应的名次
// st[i]==0: 表示i是1;  st[i]==1: 表示i是质数;  st[i]==2: 表示i是合数
int st[N];

// 埃式筛法
void init() {
    
    for (int i = 2; i < N; i++)
        if (!st[i]) {
            st[i] = 1;
            for (int j = i + i; j < N; j += i) 
                st[j] = 2;
        }
}

int main() {
    
    init();
    
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int id;
        cin >> id;
        rnk[id] = i;  // id排名是i
    }
    
    int k;
    cin >> k;
    while (k--) {
        int x;
        cin >> x;
        
        printf("%04d: ", x);
        if (!rnk[x])  // 不存在x
            printf("Are you kidding?\n");
        else if (rnk[x] == -1)  // 存在x,但是已经查过了
            printf("Checked\n");
        else {
            if (st[rnk[x]] == 0) printf("Mystery Award\n");  // 第一名
            else if (st[rnk[x]] == 1) printf("Minion\n");  // 质数
            else printf("Chocolate\n");  // 合数
            
            rnk[x] = -1;
        }
        
    }
    
    return 0;
}

AcWing 1646. 谷歌的招聘

问题描述

分析

  • 本题的思路就是遍历所有长度为K的连续数字,然后判断其是否为质数,然后输出第一个质数即可,如果都不是质数,输出404即可。

  • 数据最大为9位,即最大为 1 0 9 − 1 10^9 - 1 1091,判断质数的时间复杂度是 O ( n ) O(\sqrt{n}) O(n )的,需要30000的计算量,最多判断1000个,需要三千万的计算量,可能超时,因此需要优化。

  • 对于判断质数进行优化,我们只使用质因子进行试除法判断质数,这样判断质数的时间复杂度是是 O ( n l o g ( n ) ) O(\frac{\sqrt{n}}{log(\sqrt{n})}) O(log(n )n )的,最终大约需要三百万的计算量,可行。

代码

  • C++
#include <iostream>
#include <cstring>

using namespace std;

const int N = 40000;  // N^2 = 1.6*10^9, 足够

int n, k;
bool st[N];
int prime[N], cnt;

// 线性筛法
void init() {
    
    for (int i = 2; i < N; i++) {
        if (!st[i]) prime[cnt++] = i;
        for (int j = 0; prime[j] < N / i; j++) {
            st[prime[j] * i] = true;
            if (i % prime[j] == 0) break;
        }
    }
}

bool check(int x) {
    for (int i = 0; prime[i] <= x / prime[i]; i++)
        if (x % prime[i] == 0) 
            return false;
    return true;
}

int main() {
    
    init();
    
    string str;
    cin >> n >> k >> str;
    
    for (int i = 0; i + k <= str.size(); i++) {
        int t = stoi(str.substr(i, k));
        if (check(t)) {
            cout << str.substr(i, k) << endl;
            return 0;
        }
    }
    
    puts("404");
    
    return 0;
}
Stkcd [股票代码] ShortName [股票简称] Accper [统计截止日期] Typrep [报表类型编码] Indcd [行业代码] Indnme [行业名称] Source [公告来源] F060101B [净利润现金净含量] F060101C [净利润现金净含量TTM] F060201B [营业收入现金含量] F060201C [营业收入现金含量TTM] F060301B [营业收入现金净含量] F060301C [营业收入现金净含量TTM] F060401B [营业利润现金净含量] F060401C [营业利润现金净含量TTM] F060901B [筹资活动债权人现金净流量] F060901C [筹资活动债权人现金净流量TTM] F061001B [筹资活动股东现金净流量] F061001C [筹资活动股东现金净流量TTM] F061201B [折旧摊销] F061201C [折旧摊销TTM] F061301B [公司现金流1] F061302B [公司现金流2] F061301C [公司现金流TTM1] F061302C [公司现金流TTM2] F061401B [股权现金流1] F061402B [股权现金流2] F061401C [股权现金流TTM1] F061402C [股权现金流TTM2] F061501B [公司自由现金流(原有)] F061601B [股权自由现金流(原有)] F061701B [全部现金回收率] F061801B [营运指数] F061901B [资本支出与折旧摊销比] F062001B [现金适合比率] F062101B [现金再投资比率] F062201B [现金满足投资比率] F062301B [股权自由现金流] F062401B [企业自由现金流] Indcd1 [行业代码1] Indnme1 [行业名称1] 季度数据,所有沪深北上市公司的 分别包含excel、dta数据文件格式及其说明,便于不同软件工具对数据的分析应用 数据来源:基于上市公司年报及公告数据整理,或相关证券交易所、各部委、省、市数据 数据范围:基于沪深北证上市公司 A股(主板、中小企业板、创业板、科创板等)数据整理计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值