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

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

作者头像
落影
发布2024-02-10 08:45:46
690
发布2024-02-10 08:45:46
举报
文章被收录于专栏:落影的专栏落影的专栏

题目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
复制
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
复制
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
复制
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
复制
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
复制
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 删除。

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

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