前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >程序员进阶之算法练习(八十三)

程序员进阶之算法练习(八十三)

作者头像
落影
发布2023-08-20 08:06:37
1840
发布2023-08-20 08:06:37
举报
文章被收录于专栏:落影的专栏落影的专栏

题目1

题目链接 题目大意: 有长度为n的整数数组a,数组元素都由-1和1组成; 现在每次可以选择一个数组位置,翻转位置上元素(-1变成1,1变成-1); 假如想要实现下面的要求,最少需要多少次操作: 𝑎1+𝑎2+…+𝑎𝑛≥0 𝑎1⋅𝑎2⋅…⋅𝑎𝑛=1

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤500) 每个样例2行, 第一行,整数n (1≤n≤ 100) 第二行,n个整数;

输出: 每个样例一行,输出最小操作次数。

Examples input 7 4 -1 -1 1 -1 5 -1 -1 -1 1 1 4 -1 1 -1 1 3 -1 -1 -1 5 1 1 1 1 1 1 -1 2 -1 -1

output 1 1 0 3 0 1 2

题目解析: 我们用x来表示1的数量,y表示-1的数量; 当x<y时,需要将一部分-1变成1,这个数量是ans=(y - x + 1) / 2;(因为每次变化,都会让x和y的值差2,所以最终要除以2) 当y=y-ans后,假如y%2为1,证明最终结果还是为负数,此时需要再去掉一个-1,故而++ans;

代码语言:javascript
复制
#include<cstdio>
#include<cmath>
#include<stack>
#include<map>
#include<set>
#include<queue>
#include<deque>
#include<string>
#include<utility>
#include<sstream>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long lld;
 
class Solution {
    static const int N = 201010;
    
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            int a = 0, b = 0;
            while (n--) {
                int tmp;
                cin >> tmp;
                if (tmp == 1) ++a;
                else ++b;
            }
            int ans = 0;
            if (a < b) {
                ans = (b - a + 1) / 2;
                a += ans;
                b -= ans;
            }
            if (b % 2) {
                ++ans;
            }
            cout << ans << endl;
        }
    }
}
ac;

题目2

题目链接 题目大意: 有两个很大的整数,用字符串L和字符串R表示,其中L代表的数字小于R代表的数字; 现在想在区间[L, R]中找到2个数字,使得这两个数字的十进制表示上,每一位的绝对值差尽可能大。

十进制位数差,就是将两个整数每一位的数字进行想减,然后累加绝对值,如果位数不相同则补前导零; 比如说: 55和37,结果 |5−5|+|3−7|=4 190和209,结果 |1−2|+|9−0|+|0−9|=19 1909和90,结果 |1−0|+|9−0|+|0−9|+|9−0|=28

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤5000) 每个样例一行,字符串L和字符串R,长度不超过100;

输出: 每个样例一行,输出区间内最大的十进制位数差。

Examples input 6 53 57 179 239 13 37 132228 132228 54943329752812629795 55157581939688863366 88 1914

output 4 19 11 0 163 28

题目解析: 当只有1位时,十进制差就是个位数想减; 当有2位时,如果第一位相同,则结果为第二位相差; 如果第一位不相同,那么结果为第一位之差,加上第二位9和0的差距;因为L的第二位总是能上升到9,R的第二位总是能降低为0; 举例说明,区间[23, 46],29肯定在区间内,因为十位数2<4,那么29肯定小于46;又由于9>=3,29在区间内; 同理,46可以选出40,满足在区间内;

那么当我们找到某一位不相同时,这一位后面的数字都可以填充9999..和0000...,然后再累加上当前位数差值,就是最大的结果。

代码语言:javascript
复制
class Solution {
    static const int N = 201010;
    char a[N], b[N];
    
    
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> a >> b;
            int n = strlen(a);
            int m = strlen(b);
            int ans = 0;
            if (n < m) {
                ans = b[0] - '0' + (m - 1) * 9;
            }
            else if(n > m){
                ans = a[0] - '0' + (n - 1) * 9;
            }
            else {
                int k = -1;
                for (int i = 0; i < n; ++i) if(a[i] != b[i]) {
                    k = i;
                    break;
                }
                if (k == -1) ans = 0;
                else {
                    ans = abs(a[k] - b[k]) + (n - 1 - k) * 9;
                }
            }
            cout << ans << endl;
            
        }
    }
}
ac;

题目3

题目链接 题目大意: 有两个字符串S和T,长度均为n; 现在Alice和Bob两个人在玩有一个游戏,两个人轮流行动,Alice先手。 Alice的操作,是选择字符串S或者字符串T中的某一个元素,将其修改为任意字符; Bob的操作,是选择字符串S或者字符串T,翻转该字符串;

当字符串完全相同时,游戏结束。

Alice的目标是尽可能少行动次数,Bob的目标是尽可能多行动次数; 假设Alice和Bob的行动都是最优决策。 现在想要知道,当Alice和Bob总共需要行动多少步。

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例3行, 第一行,整数𝑛(1≤𝑛≤1e5) 第二行,字符串S; 第三行,字符串T;

输出: 每个样例一行,输出最终行动步数。

Examples input 7 5 abcde abxde 5 hello olleo 2 ab cd 7 aaaaaaa abbbbba 1 q q 6 yoyoyo oyoyoy 8 abcdefgh hguedfbh

output 1 2 3 9 0 2 6

题目解析: 先分析Alice和Bob的策略。 先从Alice的视角来看,最终游戏必然会停止,因为Alice可以把字符串S、T修改为完全相同的字符组成,这样使得Bob的翻转操作无效; 为了尽可能减少步数,必须利用Bob的翻转操作,例如说当Bob遇到abc和cba时,由于Bob必须选择一个字符串翻转,最终游戏必然会停止。 我们以字符串S为参考下,最终结果要么是字符串S、要么是字符串revertS; 计算得到字符串T和字符串S、字符串T和字符串revertS的差异部分,最终的游戏过程就是Alice修改差异部分字符,然后Bob不断翻转,最终字符串以S或者revertS结束。 (注意,这里为了简化分析,利用了两个隐性条件,Alice修改S和修改T是等价的,Bob翻转字符串S和字符串T是等价的)

不同的是,当最终以字符串S结束时,翻转次数为偶数; 当最终以字符串revertS结束时,翻转次数为奇数。

代码语言:javascript
复制
class Solution {
    static const int N = 201010;
    char a[N], b[N];
    
    
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            cin >> a >> b;
            int cnt1 = 0, cnt2 = 0;
            for (int i = 0; i < n; ++i) {
                if (a[i] != b[i]) ++cnt1;
                if (a[n - i - 1] != b[i]) ++cnt2;
            }
            
            if (cnt1 == 0) cout << 0 << endl;
            else {
                /*
                 1次,翻转成本0 0
                 2次,翻转成本2 0 21 0 12
                 3次,翻转成本2 0 21 0 12 0
                 4次,翻转成本4 0 21 0 12 0 21 0 12
                 5次,翻转成本4 0 21 0 12 0 21 0 12 0
                 */
                int ans1 = cnt1 + cnt1 / 2 * 2; //翻转成本为偶数

                /*
                 1次,翻转成本1 0 21
                 2次,翻转成本1 0 21 0
                 3次,翻转成本3 0 21 0 12 0 21
                 4次,翻转成本3 0 21 0 12 0 21 0
                 5次,翻转成本5 0 21 0 12 0 21 0 12 0 21
                 */
                cnt2 = max(1, cnt2);
                int ans2 = cnt2 + (cnt2 - 1) / 2 * 2 + 1;
                cout << min(ans1, ans2) << endl;
            }
        }
    }
}

题目4

题目链接 题目大意: 课堂上有n个学生,一共有m门课程(序号为1、2、3、、、m); 由于时间限制,每个学生只能预习序号为l[i]到r[i]的课程; 老师会询问若干个课程的预习情况,某个课程如果预习了可以得1分,没预习则扣1分; 现在想知道,分数最高者和分数最低者,两者的最大分差是多少? 注意: 1、老师可以任意询问多个课程,但每个课程只能询问1次; 2、分数可以为负数;

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例 第一行,整数𝑛 and 𝑚 (2≤𝑛≤1e5,1≤𝑚≤1e9 ) 接下来n行,每行两个整数𝑙𝑖 and 𝑟𝑖 (1≤𝑙𝑖≤𝑟𝑖≤𝑚 )

输出: 每个样例一行,输出分数最高者和分数最低者,两者的最大分差;

Examples input 6 4 8 2 6 4 8 2 7 1 5 3 3 1 3 2 3 2 2 3 5 1 5 1 5 1 5 3 5 1 1 3 3 5 5 4 7 1 7 1 3 3 3 4 5 2 4 1 3 2 4

output 6 4 0 2 12 2

题目解析: 抽象成两个线段,求某个线段去掉共同部分的最大值。 我们以线段A和B来思考,我们固定线段A,线段B有几种情况: 1、线段B与线段A相交,并且是线段B右边界超过了线段A,此时线段B的左区间越大越好; 2、线段B与线段A相交,并且是线段B左边界超过了线段A,此时线段B的右区间越小越好; 3、线段B在线段A内部,此时线段B越短越好; 4、线段B覆盖了线段A,此时可以认为是情况1或者情况2,所以当下情况可以忽略;

由上面分析可以知道,我们知道最终左区间最大、右区间最小、线段最短,这三种情况的线段必然会是最终两个线段中的一个; 那么可以选择这三条线段,再枚举其他所有线段,得到最优解。

trick1: 在处理线段相交、覆盖的时候,需要边界情况,可以枚举上面提到的多种样例情况进行测试; 比如说样例: 4 2 20 1 2 2 3

2 20 1 2 3 4

2 20 1 3 2 4

2 20 2 3 1 4

trick2: 由于数据范围较大,在枚举较大值时,用0xfffffff这种容易出错,直接用1e9+1保证结果正常;

代码语言:javascript
复制
class Solution {
    static const lld N = 201010;
    pair<lld, lld> a[N];
    
    lld look(lld x1, lld y1, lld x2, lld y2) {
        lld ret = 0;
        pair<lld, lld> p[2] = {{x1, y1}, {x2, y2}};
        sort(p, p + 2);
        // p[0].first < p[1].first
        if (p[0].second < p[1].first) {
            ret = max(p[0].second - p[0].first + 1, p[1].second - p[1].first + 1);
        }
        else if (p[0].second < p[1].second) {
            ret = max(p[1].first - p[0].first, p[1].second - p[0].second);
        }
        else {
            ret = (p[1].first - p[0].first) + (p[0].second - p[1].second);
        }
        return ret;
    }
    
    lld check(lld x, lld y, lld n) {
        lld ret = 0;
        for (lld i = 0; i < n; ++i) {
            ret = max(ret, look(x, y, a[i].first, a[i].second) * 2);
        }
        return ret;
    }
    
public:
    void solve() {
        lld t;
        cin >> t;
        while (t--) {
            lld n, m;
            cin >> n >> m;
            for (lld i = 0; i < n; ++i) cin >> a[i].first >> a[i].second;
            
            
            lld ans = 0;
        
            lld len = 1e9 + 1, x = 0, y = 0;
            for (lld i = 0; i < n; ++i) {
                if (a[i].second - a[i].first < len) {
                    x = a[i].first;
                    y = a[i].second;
                    len = y - x;
                }
            }
            ans = max(ans, check(x, y, n));
            
            x = 0, y = 0;
            for (lld i = 0; i < n; ++i) {
                if (a[i].first > x) {
                    x = a[i].first;
                    y = a[i].second;
                }
            }
            ans = max(ans, check(x, y, n));
            
            x = 0, y = 1e9 + 1;
            for (lld i = 0; i < n; ++i) {
                if (a[i].second < y) {
                    x = a[i].first;
                    y = a[i].second;
                }
            }
            ans = max(ans, check(x, y, n));
            
            cout << ans << endl;
        }
    }
}

题目5

题目链接 题目大意: 有3个正整数a、b、c,并且满足a+b=c; 现在已知a、b、c三个整数的位数,用十进制表示分别为A、B、C; 想知道所有满足a+b=c的字符串中,按照字典序,第k个字符串是什么。 以 𝐴=1 , 𝐵=1 , 𝐶=2 为例 第1个字符串是,1+9=10 第2个字符串是,2+8=10 第3个字符串是,2+9=11 ...

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤1000) 每个样例一行,整数𝐴 , 𝐵 , 𝐶 , 𝑘 (1≤𝐴,𝐵,𝐶≤6 , 1≤𝑘≤1e12 ).

输出: 每个样例一行,如果存在,得输出第k个组合; 如果不存在,则输出-1;

Examples input 7 1 1 1 9 2 2 3 1 2 2 1 1 1 5 6 42 1 6 6 10000000 5 5 6 3031568815 6 6 6 1000000000000

output 2 + 1 = 3 10 + 90 = 100 -1 9 + 99996 = 100005 -1 78506 + 28543 = 107049 -1

题目解析: 首先,我们知道数字c长度不会比数字a和数字b短,因为两个正整数相加,位数不会变成少; 其次,两个正整数相加,位数最多变长1位,因为进位不可能连续变长2位,比如说99 + 99=199,2个两位数相加,数字和最多是三位;

剩下所有的数字,总能找出A+B=C的组合。 由于要按照字典序,那么枚举A优先级最高,其次是枚举B,最后C由A+B决定,不用枚举(但要满足位数限制); 所以A首先从pow(10, A-1)开始枚举,也就是A=2则从10开始枚举,A=3则从100开始枚举; 数字B的枚举同样有个区间[minB, maxB],minB=pow(10, B-1),maxB=pow(10, B) - 1; 数字C的结果区间同样是[minC, maxC]; 如果想要满足A+B=C,那么A+maxB应该要大于等于minC,并且A+minB应该要小于等于maxC; 分析到这里,就可以很容易知道,当固定A之后,B的取值区间应该就是minC-A到maxC-A。

整个算法要枚举A的数值,每次枚举复杂度O(1),整个算法复杂度O(A);

代码语言:javascript
复制
class Solution {
    typedef long long lld;
     
    static const lld N = 201010;
    
public:
    void solve() {
        lld t;
        cin >> t;
        while (t--) {
            lld a,b,c,k;
            cin >> a >> b >> c >> k;
            if (c < a || c < b || c > (max(a, b) + 1)) {
                cout << -1 << endl;
                continue;;
            }
            
            lld find = 0;
            for (lld i = pow(10, a - 1); i < pow(10, a); ++i) {
                // i + j = l
                lld minJ = pow(10, b - 1);
                lld maxJ = pow(10, b) - 1;
                lld minL = pow(10, c - 1);
                lld maxL = pow(10, c) - 1;
                if (i + maxJ >= minL && i + minJ <= maxL) {
                    lld startJ = max(minJ, minL - i);
                    lld endJ = min(maxJ, maxL - i);
                    lld sum = endJ - startJ + 1;
                    if (sum < k) k -= sum;
                    else {
                        find = 1;
                        printf("%lld + %lld = %lld\n", i, startJ + k - 1, i + startJ + k - 1);
                        break;
                    }
                }
            }
            if (!find) cout << -1 << endl;
        }
    }
}
ac;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目1
  • 题目2
  • 题目3
  • 题目4
  • 题目5
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档