大三算法导论课小结 - 短学期综合训练后续


1. 给出 n n n,问满足 1 x + 1 y = 1 n \cfrac{1}{x}+\cfrac{1}{y}=\cfrac{1}{n} x1+y1=n1 的解有多少种?

x = n + a x=n+a x=n+a y = n + b y=n+b y=n+b,则
1 x + 1 y = 1 n \cfrac{1}{x}+\cfrac{1}{y}=\cfrac{1}{n} x1+y1=n1

1 n + a + 1 n + b = 1 n \cfrac{1}{n+a}+\cfrac{1}{n+b}=\cfrac{1}{n} n+a1+n+b1=n1

2 n + a + b n 2 + ( a + b ) n + a b = 1 n \cfrac{2n+a+b}{n^2+(a+b)n+ab}=\cfrac{1}{n} n2+(a+b)n+ab2n+a+b=n1

2 n 2 + ( a + b ) n = n 2 + ( a + b ) n + a b 2n^2+(a+b)n=n^2+(a+b)n+ab 2n2+(a+b)n=n2+(a+b)n+ab

∴ n 2 = a b \therefore n^2=ab n2=ab

通过唯一基本定理: n = p 1 k 1 × p 2 k 2 × . . . . × p m k m n=p_1^{k_1}\times p_2^{k_2}\times ....\times p_m^{k_m} n=p1k1×p2k2×....×pmkm

∴ n 2 = p 1 2 k 1 × p 2 2 k 2 × . . . . × p m 2 k m \therefore n^2=p_1^{2k_1}\times p_2^{2k_2}\times ....\times p_m^{2k_m} n2=p12k1×p22k2×....×pm2km

∴ a n s = ( 2 k 1 + 1 ) ( 2 k 2 + 1 ) . . . ( 2 k m + 1 ) \therefore ans=(2k_1+1)(2k_2+1)...(2k_m+1) ans=(2k1+1)(2k2+1)...(2km+1)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n, vis[N];
vector<int> prim;

void getPrime(int n) {
    vis[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            prim.push_back(i);
        }
        for (int p:prim) {
            if (p * i > n) break;
            vis[p * i] = 1;
            if (i % p == 0) break;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    getPrime(10000);
    int T;
    cin >> T;
    for (int cs = 1; cs <= T; cs++) {
        cin >> n;
        ll res = 1;
        for (int p : prim) {
            if (n % p == 0) {
                int cnt = 0;
                while (n % p == 0) {
                    cnt++;
                    n /= p;
                }
                res *= (2 * cnt + 1);
            }
            if (p * p > n) break;
        }
        if (n > 1) res *= 3;//2*1+1
        cout << res << endl;
    }
    return 0;
}

2. 整数划分

题目

将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,其中n1≥n2≥…≥nk≥1,k≥1。
正整数n的这种表示称为正整数n的划分。求正整数n的不同划分个数。
例如正整数6有如下11种不同的划分:
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1。

题解 - 整数划分问题

f ( n , m ) f(n,m) f(n,m) 表示当 n = ∑ m i n=\sum m_i n=mi m = max ⁡ ( m i ) m=\max(m_i) m=max(mi)时的整数 n n n 的不同划分个数

#include <bits/stdc++.h>
using namespace std;
int n;

int dfs(int n, int m) {
    if (m == 1) return 1; // 只有 {1,1,..,1}
    else if (n < m) return dfs(n,n); // 不可能存在符合的集合 缩小m的范围
    else if (n == m)
        return dfs(n, m - 1) // 拆开最大值 m 至少从m中拆出一个1
               + 1; // 存在 {n}
    else
        return dfs(n - m, m) // 符合的集合里含有最大值 m
               + dfs(n, m - 1); // 拆开最大值 m 至少从m中拆出一个1
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    while (cin >> n) {
        cout << dfs(n, n) << endl;
    }

    return 0;
}

3. 数的划分

题目

将整数n分成k份,且每份不能为空,任意两种分法不能相同(不考虑顺序)。例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;1,5,1;5,1,1;
问有多少种不同的分法。

f ( n , m ) f(n,m) f(n,m) 表示整数 n n n 分成 m m m 份在不考虑顺序下的不同分法

f ( n , m ) = f ( n − 1 , m − 1 ) + f ( n − m , m ) f(n,m)=f(n-1,m-1)+f(n-m,m) f(n,m)=f(n1,m1)+f(nm,m)

  1. n n n 份单独成一组
  2. 将第 n n n 份插入到前面 m m m 组里中的

第二种的解释 - https://blog.csdn.net/elma_tww/article/details/86538836?utm_source=app

ps:

f ( n , m ) f(n,m) f(n,m) 表示整数 n n n 分成 m m m 份在考虑顺序下的不同分法

f ( n , m ) = f ( n − 1 , m − 1 ) + f ( n − 1 , m ) f(n,m)=f(n-1,m-1)+f(n-1,m) f(n,m)=f(n1,m1)+f(n1,m)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int n, m, k;
int f[N][N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    while (cin >> n >> k) {
        f[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= k; j++) {
                if (i < j)
                    f[i][j] = 0;
                else
                    f[i][j] = f[i - 1][j - 1]  // 第i份单独成一组
                            + f[i - j][j]; // 将第i份插入到前面j组里的

            }
        }
        cout << f[n][k] << endl;
    }
    return 0;
}

4. 哈夫曼编码的一个实际应用 / 哈夫曼编码的一个实际应用(压缩)

坑点:吃回车问题

#include <bits/stdc++.h>
using namespace std;
#define between(x, a, b) (a<=x && x<=b)
int n, dfn = 0;

struct Node {
    int val, priority;// 权值 优先级
    char c;// 字符 (只对叶子节点有意义)
    Node *l, *r;

    Node(int x, char y) {
        priority = ++dfn;
        c = y, val = x;
        l = r = nullptr;
    }

    Node(Node *x, Node *y) {
        priority = ++dfn;
        val = 0;
        if (x) {
            val += x->val;
            l = x;
        }
        if (y) {
            val += y->val;
            r = y;
        }
    }
};

struct cmp {
    bool operator()(Node *a, Node *b) {
        if (a->val == b->val) {
            return a->priority > b->priority;
        }
        return a->val > b->val;
    }
};

struct Huffman {
    Node *rt;
    priority_queue<Node *, vector<Node *>, cmp> q; // 优先队列

    void build(vector<Node *> v) {
        for (auto u:v) {
            q.push(u);
        }
        while (q.size() >= 2) {
            auto x = q.top();
            q.pop();
            auto y = q.top();
            q.pop();
            q.push(new Node(x, y));
        }
        rt = q.top();
        q.pop();
    }

    // 解码 二进制解码
    int pos = 0, flag;
	// 在哈夫曼树上从根节点一直爬到叶子节点
    void dfs(Node *u, vector<int> &bit) {
        if (!u->l && !u->r) {
            cout << u->c;
            return;
        }
        if (pos >= bit.size()) { // 下标越界 不存在字符
            flag = 0;
            return;
        }
        if (!bit[pos++]) {//0
            if (u->l) dfs(u->l, bit);
            else flag = 0;
        } else {
            if (u->r) dfs(u->r, bit);
            else flag = 0;
        }
    }

    void decode(vector<int> bit) {
        flag = 1, pos = 0; // flag 错误标志 pos 当前读到的二进制的位置
        int n = bit.size();
        while (pos < n && flag) {
            dfs(rt, bit);
        }
        if (!flag) cout << "||Error !";
        cout << endl;
    }

    //  编码 根据文本返回二进制编码  (以字符串的形式)
    map<char, string> mp;

    void getMapNode() {
        mapDfs(rt, "");
    }

    void mapDfs(Node *u, string s) {
        if (!u->l && !u->r) {
            mp[u->c] = s;
            return;
        }
        if (u->l) mapDfs(u->l, s + "0");
        if (u->r) mapDfs(u->r, s + "1");
    }

    string code(string s) {
        string res = "";
        for (char c:s) {
            res += mp[c];
        }
        while (res.size() % 8) res += "0";
        return res;
    }
};

int bit[5];
// 16进制转2进制 - 将十六进制的每个数都拆成4位二进制
vector<int> toBinary(string s) {
    vector<int> res;
    int x;
    for (char c:s) {
        if (between(c, '0', '9')) x = c - '0';
        else x = c - 'A' + 10;

        fill(bit, bit + 5, 0);
        int i = 0;
        while (x) {
            bit[++i] = x % 2;
            x /= 2;
        }
        i = 4;
        while (i) res.push_back(bit[i--]);
    }
    return res;
}

// 2进制转16进制
string toHex(string s) {
    string res = "";
    int n = s.length();
    for (int i = 0; i < n; i += 4) {
        int x = 0;
        for (int j = 0; j < 4; ++j) {
            x = x * 2 + s[i + j] - '0';
        }

        if (between(x, 0, 9)) res += (char) (x + (int) '0');
        else res += (char) (x - 10 + (int) 'A');
    }
    return res;
}

string s;

void Solve_Code() {
    dfn = 0;//时间序

    getline(cin, s);
    n = s.length();
    vector<Node *> v;// 结点指针集合
    for (int i = 0, x; i < n; ++i) {
        cin >> x;
        v.push_back(new Node(x, s[i]));
    }

    Huffman huffman;
    huffman.build(v);//构建哈夫曼树
    huffman.getMapNode();//获得每个叶子节点的二进制编码值

    cin >> n;
    getline(cin, s);//吃回车

    for (int i = 1; i <= n; ++i) {
        getline(cin, s);
        cout << toHex(huffman.code(s)) << endl;
    }
}

void Solve_Decode() {
    dfn = 0;//时间序

    getline(cin, s);
    n = s.length();
    vector<Node *> v;// 结点指针集合
    for (int i = 0, x; i < n; i++) {
        cin >> x;
        v.push_back(new Node(x, s[i]));
    }
    Huffman huffman;
    huffman.build(v);//构建哈夫曼树
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> s;
        huffman.decode(toBinary(s));//先将16进制转化为2进制 再拿二进制去爬哈夫曼树
    }
}

void Run() {
    int T;
    cin >> T;
    getline(cin, s);//吃回车
    for (int cs = 1; cs <= T; cs++) {
        Solve_Code();
        //Solve_Decode(); // 解码时 前面吃回车的getline的位置要改到for里面来
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    Run();

    return 0;
}

5. 1x1、2x2、3x3、4x4、5x5、6x6盒子打包问题

#include <bits/stdc++.h>
using namespace std;
int n, m, k;
int cnt[20];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    while (true) {
        n = 0;
        for (int i = 1; i <= 6; i++) {
            cin >> cnt[i];
            n += cnt[i];
        }
        if (n == 0) break;

        int res = 0;
        int num1 = 0, num2 = 0, tmp;//留给1x1 2x2的剩余位置个数
        res += cnt[6];// 6x6 5x5 4x4 的盒子一定是单独放一个盒子的
        res += cnt[5];
        res += cnt[4];
        num2 += cnt[4] * 5;// 4x4剩余部分可以放5个2x2
        res += cnt[3] / 4;

        tmp = cnt[3] % 4;
        if (tmp) { // 根据3x3剩余的位置 判断可以给2x2留下最多多少个位置
            res++;
            if (tmp == 1) num2 += 5; //注意这里
            else if (tmp == 2) num2 += 3;
            else num2 += 1;
        }

        if (cnt[2] > num2) {
            tmp = cnt[2] - num2;
            res += tmp / 9; // 单独装 2x2

            tmp %= 9;
            if (tmp) res++;
        }

        num1 = res * 36 - cnt[6] * 36 - cnt[5] * 25 - cnt[4] * 16 - cnt[3] * 9 - cnt[2] * 4;
        if (cnt[1] > num1) {
            tmp = cnt[1] - num1;
            res += tmp / 36;

            tmp %= 36;
            if (tmp) res++;
        }
        cout << res << endl;
    }
    return 0;
}

6. 基站安装

#include <bits/stdc++.h>
using namespace std;
int n, m, k;
double d;

struct Seg {
    double S, T;

    bool operator<(const Seg oth) const {
        return S < oth.S;
    }
} seg[1005];

struct Point {
    double x, y;

    void read() {
        cin >> x >> y;
    }

    bool isLegal() {
        return !(y > d);
    }

    bool operator<(const Point oth) const {
        if (x == oth.x) return y > oth.y;
        return x < oth.x;
    }

    Seg getSeg() {
        return {
                x - sqrt(d * d - y * y),
                x + sqrt(d * d - y * y)
        };
    }

} p[1005];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int cs = 0;
    while (cin >> n >> d) {
        if (n == 0 && d == 0) {
            break;
        }

        int flag = 0;
        for (int i = 1; i <= n; i++) {
            p[i].read();
            if (!p[i].isLegal()) {
                flag = 1;
            }
        }

        cout << "Case " << ++cs << ": " ;
        if (flag) {
            cout << -1 << endl;
        } else {
            for (int i = 1; i <= n; i++) {
                seg[i] = p[i].getSeg();
            }
            sort(seg + 1, seg + 1 + n);
            double l = seg[1].S, r = seg[1].T;
            int res = 1;
            for (int i = 2; i <= n; i++) {
                l = max(l, seg[i].S);
                r = min(r, seg[i].T);
                if (l > r) {
                    l = seg[i].S;
                    r = seg[i].T;
                    res++;
                }
            }
            cout << res << endl;
        }
    }
    return 0;
}

7. 军训选人问题

n个人,给出每个人的身高,找到最长连续区间,使得区间的最大值与最小值的差值小于d

n ≤ 1 e 5 , 140 ≤ h ≤ 250 n\le1e5,140\le h\le 250 n1e5,140h250

坑点:
1.RMQ爆空间
2.数据太水了,n2暴力都给过了,数据要是严一点分分钟卡掉(lll¬ω¬)

正解:
multiset(一个优化的二叉树,下面简称mst)+双指针
将区间内的所有元素都放到mst里,
区间最小值就是二叉树上最左边的点:*mst.begin()
区间最大值就是树上最右边的点:*(--mst.end())

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, d;
multiset<int> mst;
int a[N];

void Solve() {
    while (cin >> n >> d) {
        mst.clear();
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        int S = 0, T = 0;
        for (int r = 1, l = 1; r <= n; r++) {
            mst.insert(a[r]);
            while (l < r && *(--mst.end()) - *mst.begin() > d) {
                mst.erase(mst.find(a[l++])); // mst里删除啊a[l]、
                // 注意:不能写成mst.erase(a[l]) 会将所有与a[l]相同的点都删除的
            }
            if (T - S + 1 < r - l + 1) {
                S = l, T = r;
            }
        }
        cout << "From=" << S << ",To=" << T << endl;
        cout << "MaxLen=" << T - S + 1 << endl;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    Solve();
    
    return 0;
}

8. 军训选人问题(续)

n个人取长度为m的连续区间,问区间内最大值-最小值的差值的最小值

滑动区间+multiset,这个就比上面简单了

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, m;
multiset<int> mst;

void Solve() {
    while (cin >> n >> m) {
        mst.clear();

        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
        }

        int S = 0, T = 0, diff = 0x3f3f3f3f;
        for (int r = 1, l = 1; r <= n; ++r) {
            mst.insert(a[r]);
            while (l < r && r - l + 1 > m) {
                auto it = mst.find(a[l]);
                mst.erase(it);
                l++;
            }
            if (r - l + 1 == m) {
                int x = *(--mst.end()) - *mst.begin();
                if (x < diff) {
                    diff = x;
                    S = l, T = r;
                }
            }
        }

        if (n >= m) {
            cout << "From=" << S << ",To=" << T << endl;
            cout << "MinDiff=" << diff << endl;
        } else {
            cout << "No solution !" << endl;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    Solve();

    return 0;
}

9. 最长公共子序列(输出公共序列)

输出路径就是在二维矩阵上从坐标n,m按照状态转移往回爬

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
const int N = 1e3 + 10;
int n, m, k;
string A, B;
pii pre[N][N];

void out(int i, int j) {
    if (!i || !j) return;
    out(pre[i][j].first, pre[i][j].second);
    if (A[i] == B[j]) {
        cout << A[i];
    }
}

void Solve() {
    cin >> A >> B;
    n = A.length(), m = B.length();
    A = " " + A, B = " " + B;
    vector<vector<int>> f(n + 2, vector<int>(m + 1, 0));

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (A[i] == B[j]) {// 如果相等 一定会增大 f一定最优
                f[i][j] = f[i - 1][j - 1] + 1;
                pre[i][j] = {i - 1, j - 1};
                
            }else if (f[i - 1][j] >= f[i][j - 1]) {
                f[i][j] = f[i - 1][j];
                pre[i][j] = {i - 1, j};
                
            } else {
                f[i][j] = f[i][j - 1];
                pre[i][j] = {i, j - 1};
            }
        }
    }

    cout << f[n][m] << endl;
    out(n, m);
    cout << endl;
}

void Run() {
    int T;
    cin >> T;
    for (int cs = 1; cs <= T; cs++) {
        //cout << "Case " << cs << ": ";//printf("Case %d: ", cs);
        Solve();
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    Run();
    
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值