Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >程序员进阶之算法练习(九十七)

程序员进阶之算法练习(九十七)

作者头像
落影
发布于 2024-02-10 00:45:46
发布于 2024-02-10 00:45:46
11300
代码可运行
举报
文章被收录于专栏:落影的专栏落影的专栏
运行总次数:0
代码可运行

题目1

题目链接 题目大意: 有这样的一个字符串,如图:

现在光标停留在最左边的数字1处,我们可以进行以下的操作: 1、将当前光标所在位置的数字输出; 2、移动光标到相邻的数字,比如说从1移动到2,从2移动到3;(1的左边不能移动,0的右边不能移动)

现在想知道,输出特定的4个字符,最少需要几次操作。

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例一行,4个整数;

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

Examples input 10 1111 1236 1010 1920 9273 0000 7492 8543 0294 8361

output 4 9 31 27 28 13 25 16 33 24

题目解析: 输出数字的最小操作方法: 1、将光标移到指定位置; 2、展示当前数字;

题目的意思非常简单,但是如果直接通过if去实现,在计算0的位置时,会比较繁琐;(因为数字0在最右边,破坏了字符和数字的对应关系) 这里有个实现的小技巧,我们将数字0看成10,那么整个数字序列就是从小到大。 这样在计算操作1的时候,就能通过数字相减直接得到结果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            char s[10];
            cin >> s;
            int cur = 1, ans = 4;
            for (int i = 0; i < 4; ++i) {
                int idx = s[i] - '0';
                if (idx == 0) idx += 10;
                ans += abs(cur - idx);
                cur = idx;
            }
            cout << ans << endl;
        }
    }
}
ac;

题目2

题目链接 题目大意: 给出一个长度为n的字符串s,现在需要移除字符串中的k个字符,剩下的字符可以随意排列; 问,剩下的字符能否组成一个回文串?

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例2行 第一行,𝑛 and 𝑘 (0≤𝑘<𝑛≤1e5) 第二行,字符串s;(小写字母组成)

输出: 每个样例一行,如果有解则输出YES;如果无解则输出NO;

Examples input 14 1 0 a 2 0 ab 2 1 ba 3 1 abb 3 2 abc 6 2 bacacd 6 2 fagbza 6 2 zwaafa 7 2 taagaak 14 3 ttrraakkttoorr 5 3 debdb 5 4 ecadc 5 3 debca 5 3 abaac

output YES NO YES YES YES YES NO NO YES YES YES YES NO YES

题目解析: 最终的字符串可以任意调整顺序,那么重点在于每个字符的数量; 题目是要求构造回文串,如果某个字符数量是偶数,那么可以组成回文串;如果某个字符数量是奇数,那可能会导致无法构成回文串。 假设统计所有字符的数量,有x个偶数字符,有y个奇数字符;那么能构成回文串的条件就是y<=1;(如果只有1个奇数,可以把多出来这个字符放在回文串中间) 由于题目增加了一个限制,要去除k个字符,那么奇数字符就可以有更多(因为移除时优先移除奇数字符),所以最终条件是y<=1+k;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
    static const int N = 201010;
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            string s;
            cin >> n >> k;
            cin >> s;
            vector<int> cnt(26);
            for (int i = 0; i < n; ++i) cnt[s[i] - 'a']++;
            int y = 0;
            for (int i = 0; i < 26; ++i) y += (cnt[i] % 2);
            if (y <= 1 + k) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
    }
}
ac;

题目3

题目链接 题目大意: 有n个整数的数组a,再给出整数k; 现在可以进行任意次以下操作: 选择某个数组元素,将该元素+1;

现在要求数组最终的乘积,能够整除数字k,问最少需要操作多少次;

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例2行 第一行,𝑛 and 𝑘 (2≤𝑛≤1e5 , 2≤𝑘≤5 ) 第二行,n个整数𝑎1,𝑎2,…,𝑎𝑛(1≤𝑎𝑖≤10).

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

Examples input 15 2 5 7 3 3 3 7 4 1 5 2 9 7 7 3 9 5 5 5 4 1 2 3 7 4 9 5 1 5 9 5 1 3 4 6 3 6 3 4 6 1 5 3 4 1 5 9 4 4 1 4 1 1 3 4 3 5 3 4 5 8 9 9 3 2 5 1 6 2 5 10 10 4 5 1 6 1 1 2 5 7 7 output 2 2 1 0 2 0 1 2 0 1 1 4 0 4 3

题目解析: 第一反应,是将k进行因数分解,然后将数字分配到对应因数,还要考虑一个数字对应多个因数的情况(比如说a1=25,可以对应到两个因数5); 这样题目整体处理会比较麻烦。 考虑到k的数据范围很小,因数情况也只有4=2x2的可能,可以不使用这种方法来处理。

假如k=2时,如果数组a存在偶数,则ans=0,否则ans=1; 假如k=3时,判断每个数组元素与3的余数即可,如果有能整除,则ans=0,否则为ans=3-最大余数; 假如k=4时,按照2的因数来算,比如说4就算2个,如果数组中存在2个,那么ans=0;如果数组中存在1个,那么ans=1(因为必然还有一个奇数,这个奇数可以+1得到偶数);如果数组中存在0个,那么ans=2(因为有两个数字,必定是2个奇数);(同时也可以按照k=3的做法,计算下如果累加每个数字的情况,取较小者) 假如k=5时,可以按照k=3的做法来;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
    static const int N = 201010;
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            cin >> n >> k;
            int ans = 0, cnt = 0;
            while (n--) {
                int tmp;
                cin >> tmp;
                if (tmp % k) ans = max(ans, tmp % k);
                else ans = k;
                while (tmp % 2 == 0) {
                    ++cnt;
                    tmp /= 2;
                }
            }
            if (k == 4) {
                if (cnt >= 2) cout << 0 << endl;
                else cout << min(k - ans, 2 - cnt) << endl;
            }
            else {
                cout << (k - ans) << endl;
            }
        }
    }
}
ac;

题目4

题目链接 题目大意: 有n个整数的数组𝑎1,𝑎2,…,𝑎𝑛 现在可以对数组进行多次下述操作: 选择数组中的某个整数𝑎𝑖,如果𝑎𝑖<0 则可以把𝑎𝑖替换为[𝑎𝑖,0]区间中的整数;如果𝑎𝑖>0,则可以把𝑎𝑖替换为 [0,𝑎𝑖] 区间中的整数.

现在想知道最少需要多少次操作,才能使得最终数组所有数字的乘积尽可能的小;

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤500) 每个样例2行 第一行,整数𝑛(1≤𝑛≤100) 第二行,n个整数𝑎1,𝑎2,…,𝑎𝑛(−1e9≤𝑎𝑖≤1e9).

输出: 每个样例第一行,输出需要的操作次数x; 接下来每个操作一行,输出参数i和x,表示将a[i]设置为x;

Examples input 4 1 155 4 2 8 -1 3 4 -1 0 -2 -5 4 -15 -75 -25 -30

output 1 1 0 0 0 1 3 0

题目解析: 题目的要求的是乘积尽可能小,如果数组当前乘积结果是正数,那么将任意一个数字变为0,结果就是最小值0; 如果当前乘积是数字0,那么不管如何修改,最终结果都是0; 如果当前乘积是数字负数,那么修改任何数字,都可能会让结果更大,而不是更小。

思路想清楚,代码就很简单了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            int zero = 0, cnt = 0;
            for (int i = 0; i < n; ++i) cin >> a[i];
            for (int i = 0; i < n; ++i) {
                if (a[i] == 0) ++zero;
                else if (a[i] < 0) ++cnt;
            }
            if (cnt % 2 || zero) cout << 0 << endl;
            else cout << "1\n 1 0" << endl;
        }
    }
}
ac;

题目5

题目链接 题目大意: 有n个整数区间[𝑙1,𝑟1],[𝑙2,𝑟2],…,[𝑙𝑛,𝑟𝑛],每个区间有一个系数 𝑐𝑖,每个区间的重量为𝑐𝑖⋅(𝑟𝑖−𝑙𝑖). 现在可以进行下列操作: 1、任意调换n个整数区间,左区间l的数字; 2、任意调换n个整数区间,右区间r的数字; 3、任意调换n个整数区间,系数c的数字; 要求调换完,每个区间 [𝑙𝑖, 𝑟𝑖] 都满足 𝑙𝑖 < 𝑟𝑖;

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例有4行 第一行,整数 𝑛 (1≤𝑛≤2e5) 第二行,整数 𝑙1,𝑙2,…,𝑙𝑛(1≤𝑙𝑖≤2⋅1e5) 第三行,整数 𝑟1,𝑟2,…,𝑟𝑛 (𝑙𝑖<𝑟𝑖≤2⋅1e5) 第四行,整数𝑐1,𝑐2,…,𝑐𝑛 (1≤𝑐𝑖≤1e7 )

题目保证,{𝑙1,𝑙2,…,𝑙𝑛,𝑟1,𝑟2,…,𝑟𝑛}数字各不相同。

输出: 每个样例一行,输出调整后,所有区间的最小重量和。

Examples input 2 2 8 3 12 23 100 100 4 20 1 2 5 30 4 3 10 2 3 2 3

output 2400 42

题目解析: 题目允许对l、r、c进行任意调换,那么可以先对l、r、c进行从小到大排序,便于分析问题。 接下来做数据分析,先考虑n=2的情况 a1 a2 b1 b2 c1 c2 正常情况下 (b1-a1)c1 + (b2 - a2)c2 先考虑c1和c2的情况,假设b1-a1和b2-a2相等,那么调换c1和c2对于结果没有影响。 假设b1-a1<b2-a2呢? 这次c1<c2就会导致更小的结果,应该把更大的数字放在前面。

延伸分析,假设有若干个区间长度,那么可以将区间长度从小到大排序,接着把c越大的匹配到越前面的结果。

接下来,就是怎么匹配出来若干个区间长度,并且保证结果符合要求。 首先还是从2个区间开始分析 a1 a2 b1 b2 我们知道最终两个区间的和,不管如何交换,必然是 (b1 - a1) + (b2 - a2)。 那在这种情况下,我们让其中一个区间尽可能小,另外一个区间必然会增大。

沿着这个贪心思路,我们只需要每次让右区间,去匹配到尽可能接近的左区间。 有一个简单实现方案,对于每一个左区间(从大到小开始),我们从大到小去遍历右区间,找出来一个最近的节点。 但是这样的复杂度是O(N x N);

我们可以引入优先队列来简化操作,在选择右区间的时候,big队列表示前面选择过的节点队列,从大到小排列,这样就可以直接从big队列中找到之前遍历过的最大值; backup表示还没有被选择过的节点,从小到大排列,这样当big队列最大值都无法满足要求,就需要从backup中取数字。 这样整体的复杂度就可以降低到NlogN的级别。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
    static const int N = 201010;
    priority_queue<int> big;
    priority_queue<int, vector<int>, greater<int> > backup;
    int a[N], b[N], c[N], diff[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            for (int i = 0; i < n; ++i) cin >> a[i];
            for (int i = 0; i < n; ++i) cin >> b[i];
            for (int i = 0; i < n; ++i) cin >> c[i];
            sort(a, a + n);
            sort(c, c + n);
            for (int i = 0; i < n; ++i) backup.push(b[i]);
            for (int i = n - 1; i >= 0; --i) {
                int cur = 0;
                if (!big.empty()) {
                    cur = big.top();
                }
                if (cur < a[i]) { // 当前big堆里不满足
                    while (cur < a[i]) {
                        cur = backup.top();
                        big.push(cur);
                        backup.pop();
                    }
                    big.pop();
                }
                else {
                    big.pop();
                    while (big.size() && big.top() > a[i]) {
                        backup.push(cur);
                        cur = big.top();
                        big.pop();
                    }
                }
                diff[i] = cur - a[i];
            }
            sort(diff, diff + n);
            lld ans = 0;
            for (int i = 0; i < n; ++i) ans += diff[i] * 1LL * c[n - 1 - i];
            cout << ans << endl;
        }
    }
}
ac;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
程序员进阶之算法练习(九十五)
题目链接 题目大意: 有n个整数的数组a,现在需要构造一个1到n的排列b; 令𝑐𝑖 = 𝑎𝑖−𝑏𝑖,希望最终𝑐𝑖 出现尽可能多的不同整数。
落影
2024/01/06
1290
程序员进阶之算法练习(九十五)
程序员进阶之算法练习(九十九)
题目链接 题目大意: 有三个长度为n的字符串a、b、c,字符串都是小写字符; 有一个长度为n的模版t,模版会与字符串a、b、c匹配,匹配规则如下: 1、假如模版的字符为小写字母,则对应位置必须是相同字符才算匹配; 2、假如模版的字符为大写字母,则对应位置则不能是相同字符才算匹配; 比如说模板abc和字符串abc是匹配的,模板ABC和字符串def也是匹配的,模板ABC和字符串abc是不匹配的;
落影
2024/02/25
1390
程序员进阶之算法练习(六十七)
题目链接 题目大意: 给出n个整数的数组a和b,现在可以执行任意次下面的操作: 选择索引x,交换数组a和b中的元素a[x]和b[x];
落影
2022/10/04
2390
程序员进阶之算法练习(八十一)
题目链接 题目大意: 给出n个整数的数组,现在可以对数组进行以下操作: 选择数组中任意两个不同的整数a[i]和a[j],令a[i]=x,a[j]=y,其中满足x*y = a[i] * a[j];
落影
2023/08/09
3530
程序员进阶之算法练习(六十三)
按照上面的规则,迭代k次之后,得到a[k]; 现在给出a[1]和k,求最终迭代出来的a[k]值是多少?
落影
2022/09/02
2490
程序员进阶之算法练习(六十三)
程序员进阶之算法练习(八十七)
题目链接 题目大意: 给出一个整数的数组,长度为n; 现在可以进行以下的操作: 选择长度不小于2的区间[l, r],将区间内的整数依次进行异或操作,然后将得到的整数替换区间所有的数字;
落影
2023/10/18
1970
程序员进阶之算法练习(八十七)
程序员进阶之算法练习(八十五)
题目链接 题目大意: 有n个整数的数组a,现在需要给数组每个元素进行染色,注意: 1、每个元素只能有一个颜色; 2、每个元素都要染色; 每个颜色的收益,等于染该色的元素中最大值减去最小值; 问,染色完所有元素后,最大的收益是多少。
落影
2023/09/16
1800
程序员进阶之算法练习(七十九)
题目链接 题目大意: 有n个人参加投票,小明是第一个; 投票一共k轮,每轮每个人会做出一个选择,分别用+和-表示,那么一共有三个结果: +的人数大于-的人数,那么-的人出局; -的人数大于+的人数,那么+的人出局; 如果+和-的人数一样多,那么所有人出局; 出局的人,不再参与后续投票。
落影
2023/07/09
1560
程序员进阶之算法练习(一百)
题目链接 题目大意: 给出一个整数,问该整数能否切分为两个数字a和b,满足: 1、a和b都不包括前导零,是一个正常的数字; 2、a和b都大于0; 3、b > a;
落影
2024/03/09
1170
程序员进阶之算法练习(九十四)
题目链接 题目大意: 有n个整数组成的数组a,现在可以对数组a的元素任意打乱顺序,要求满足: 假设打乱后的数组是b,要满足:𝑏1+𝑏2=𝑏2+𝑏3=…=𝑏𝑛−1+𝑏𝑛=k 也就是相邻两个数字的和相同。
落影
2023/12/23
1050
程序员进阶之算法练习(九十四)
程序员进阶之算法练习(八十三)
题目链接 题目大意: 有长度为n的整数数组a,数组元素都由-1和1组成; 现在每次可以选择一个数组位置,翻转位置上元素(-1变成1,1变成-1); 假如想要实现下面的要求,最少需要多少次操作: 𝑎1+𝑎2+…+𝑎𝑛≥0 𝑎1⋅𝑎2⋅…⋅𝑎𝑛=1
落影
2023/08/20
2430
程序员进阶之算法练习(八十)
题目链接 题目大意: 有一个数组a,仅有整数1和-1组成,我们定义数组a的乘积为: 对于 1≤𝑖<𝑗≤𝑛, 𝑎[𝑖]⋅𝑎[𝑗]=1的数量。
落影
2023/07/25
1930
程序员进阶之算法练习(六十八)
题目1 题目链接 题目大意: 有n个球,序号分别是1、2、3、、、、n,每个球上面有一个数字a[1]、a[2]、a[3]、、、a[n],这组数字是不递减的,即是 a[i]≤a[i+1], 1≤i<𝑛; 现在需要给n个球染色,需要满足: 1、每个球只有一个颜色; 2、将某个颜色的球挑选出来,按照序号从小到大放好,上面的数字是严格递增; 问,最少需要几个颜色。 输入: 第一行是𝑡,表示样例数 (1≤𝑡≤100) 每个样例两行,第一行是整数𝑛 (1≤𝑛≤100) 第二行是n个整数 𝑎1,𝑎2,…
落影
2022/10/04
2310
程序员进阶之算法练习(八十二)
题目链接 题目大意: 给出一个整数n,构造一个长度为n的整数数组a,满足: 1、1≤𝑎𝑖≤1000 对于所有的 1≤𝑖≤𝑛; 2、𝑎𝑖 能整除𝑖,对于所有的 1≤𝑖≤𝑛; 3、𝑎1+𝑎2+…+𝑎𝑛 能够整除 𝑛 .
落影
2023/08/13
2060
程序员进阶之算法练习(七十七)
题目链接 题目大意: 给出n个整数的数组a,数组的元素可能是1或者2; 现在想要找到一个最小的位置k满足: 𝑎1⋅𝑎2⋅…⋅𝑎𝑘=𝑎𝑘+1⋅𝑎𝑘+2⋅…⋅𝑎𝑛
落影
2023/05/27
2070
程序员进阶之算法练习(六十二)AK练习
题目链接 题目大意: 小明有a个1元硬币,b个2元硬币; 小明想要购买一个商品,并且不想找零; 现在小明想知道自己无法给到最低价格是多少;
落影
2022/06/05
5450
程序员进阶之算法练习(九十三)
题目链接 题目大意: 有3个字符a/b/c,排成一行; 现在可以选择两个字符,交换对应的位置; 上述操作最多只能执行一次,问能否得到abc的顺序。
落影
2023/12/18
1490
程序员进阶之算法练习(七十二)
题目链接 题目大意: 给出一个字符串,由小写字母组成; 现在Alice和Bob在玩游戏,轮流从字符串中移除一个子串,Alice先操作; Alice允许移除偶数长度子串,Bob允许移除奇数长度子串;(也允许不移除) 最终看每个人移除子串的分数总和,字母a是1分,b是2分、、、z是26分; 问最终谁能赢得游戏,以及胜者领先的分数;
落影
2023/03/07
2680
程序员进阶之算法练习(九十八)
题目链接 题目大意: 在一个国际象棋的棋盘上,有一个棋子,它的移动规则类似马,能够朝着横or竖方向移动距离a,然后朝竖or横(和之前不同)移动距离b; 比如说马的移动规则就是a=1,b=2;
落影
2024/02/18
1890
程序员进阶之算法练习(九十八)
程序员进阶之算法练习(七十三)
题目链接 题目大意: 有两种车分别有4个轮子和6个轮子,现在只知道若干个车的轮子总数,想知道最少和最多有几辆车;
落影
2023/03/07
3030
相关推荐
程序员进阶之算法练习(九十五)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验