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

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

作者头像
落影
发布2023-12-18 12:15:35
1080
发布2023-12-18 12:15:35
举报
文章被收录于专栏:落影的专栏落影的专栏

题目1

题目链接 题目大意: 有3个字符a/b/c,排成一行; 现在可以选择两个字符,交换对应的位置; 上述操作最多只能执行一次,问能否得到abc的顺序。

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤6) 每个样例一行,字符串abc的随机排列;

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

Examples input 6 abc acb bac bca cab cba

output YES YES YES NO NO YES

题目解析: 将字符串与abc进行匹配,计算得到一共有x个位置的字符匹配; 如果x=3,则全部字符都匹配了,不需要操作; 如果x=1,则表示有2个字符不匹配,那么交换这两个字符; 其他情况则直接输出NO;

代码语言: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 cnt = 0;
            for (int i = 0; i < 3; ++i) if (s[i] == 'a' + i) ++cnt;
            cout << ((cnt == 1 || cnt == 3) ? "YES":"NO") << endl;
        }
    }
}
ac;

题目2

题目链接 题目大意: 给出一个字符串s,现在可以进行以下操作: 1、将某个子串AB,替换成子串BC; 2、将某个子串BA,替换成子串CB;

现在想知道,最多可以对字符串进行多少次操作。

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例一行,字符串𝑠 ,只有字符A和B (1≤|𝑠|≤2⋅1e5)

输出: 每个样例一行,输出可以最多可以执行的操作数。

Examples input 8 ABBA ABA BAABA ABB AAAAAAB BABA B AAA

output 2 1 3 1 6 2 0 0

题目解析: 假设原来字符串是xxxAByyy,进行一次操作1之后,会变成xxxBCyyy; 容易知道,字符C会成为阻断,yyy无法与C形成搭配,但是xxxB仍然可能会产生操作1,比如说AAAB这样的字符串就可以连续执行操作1; 同理,BAAA可以连续执行操作2;

那么将连续的A聚合起来,题目的要求,就变成如何分配B给连续A,使得最终的结果最大; 对于 ABABABA的这样字符,那么从中选择一个最小的A即可。 但是对于BABA、ABBA这样的字符呢?

为了方便计算,我们可以用字符B来分割原字符串。 如果遇到ABBA这样的字符,我们假设在BB中间插入一个A(0)的字符,那么整个算法就完善起来: 整个字符串都可以抽象成这样的的字符组合:Ax B Ax B Ax ..... (Ax表示有连续x个字符A) 比如说BAABA就可以表示为 [A0,B,A2,B,A1],容易知道最终A0是最小,那么结果就是0+2+1-0=3;

代码语言:javascript
复制
class Solution {
    static const int N = 201010;
    string s;
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> s;
            int n = s.length();
            int cur = 0;
            vector<int> vec;
            for (int i = 0; i < n; ++i) {
                if (s[i] == 'B') {
                    vec.push_back(cur);
                    cur = 0;
                }
                else {
                    ++cur;
                }
            }
            if (cur != 0 || s[n - 1] == 'B') {
                vec.push_back(cur);
            }
            sort(vec.begin(), vec.end());
            int ans = 0;
            for (int i = 0; i < vec.size(); ++i) {
                ans += vec[i];
            }
            ans -= vec[0];
            cout << ans << endl;
        }
    }
}

题目3

题目链接 题目大意: Alice和Bob在玩游戏。 初始有n个数字1,每次可以选择2个或者以上相同的数字,将这些数字移除,然后添加这些数字的和; 比如说[1, 1, 1, 1],可以选择2个1合并,得到[2, 1, 1]; 现在Alice和Bob轮流进行操作,Alice先手; 如果遇到没有2个相同的数字,那么该轮选手获胜。

输入整数n,表示有n个数字1; 输出Alice或者Bob,表示获胜者;

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤99) 每个样例一行,整数𝑛 (2≤𝑛≤200)

输出: 每个样例一行,输出获胜者。

Examples input 2 3 6

output Bob Alice

题目解析:

先从小样例入手: n=2,[1, 1] = B n=3,1,1,1 = B n=4,1,1,1,1 = B n=5,1,1,1,1,1 = 1,1 + 3 = A n=6,1,1,1,1,1,1 = 1,1 + 4 = A ... 这里比较容易得到一个必胜策略,只需要拆分为 [1,1] + (n-2) = A,并且n-2比2大即可。

代码语言:javascript
复制
class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            cout << (n > 4 ? "Alice" : "Bob") << endl;
        }
    }
}
ac;

题目4

题目链接 题目大意: 某个数组被定义为beautiful,只要满足: 将数组前面连续的一段(长度可以是0)切下来,拼接到数组最后面,数组最终是非递减的,那么数组是beautiful。

现在有一个数组,初始化状态为空; 依次给出n个整数,如果某个整数添加到数组末尾后数组是beautiful,那么该整数必须添加到数组末尾,否则放弃; 问最终由有哪些数字会添加到数组中。

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

输出: 每个样例一行,由01组成长度为n的字符串;第i个字符为1表示第i个字符会被添加到数组,为0则表示不会;

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

output 111110010 11111 11011

题目解析: 按照题目的要求,在过程中并没有决策的空间,那么关键点就是如何快速得到这个结果。

1、当a[i+1] >= a[i],继续保持; 2、当a[i+1] < a[i],首次出现就要进行分割,a[i+1]要放在数组末尾,如果a[1] >= a[i],那么a[i]可行; 接下来要满足,所有数字大于等于a[i]并且小于等于a[1];

这里犯了一次低级错误 cur = a[i]写成了cur = ans[i]; 导致出现几次Wrong Answer,后面名字要有差异。

代码语言:javascript
复制
class Solution {
    static const int N = 201010;
    int a[N], ans[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            bool rev = 0;
            int cur = 0;
            memset(ans, 0, sizeof(ans));
            for (int i = 0; i < n; ++i) {
                cin >> a[i];
                if (i == 0) {
                    ans[i] = 1;
                    cur = a[i];
                }
                else {
                    if (rev) {
                        if (a[i] >= cur && a[i] <= a[0]) {
                            cur = a[i];
                            ans[i] = 1;
                        }
                    }
                    else {
                        //  0 4 15 18 4 10 12 8 13 8 15 0 14 12 10 12 10 14 13
                        if (a[i] >= cur) {
                            ans[i] = 1;
                            cur = a[i];
                        }
                        else if (i == 1 || a[0] >= a[i]){
                            cur = a[i];
                            rev = 1;
                            ans[i] = 1;
                        }
                    }
                }
            }
            for (int i = 0; i < n; ++i) putchar('0' + ans[i]);
            puts("");
        }
    }
}

题目5

题目链接 题目大意: 有长度为n的字符串s,由字符A/B/C/D/E组成; 现在将字符串按照下述规则转成数字: 1、A、B、C、D、E分别代表数字1、10、100、1000、10000; 2、如果某个字符,右边的位置存在一个字符比当前字符更大,则添加负号;(比如说AB,A的右边存在B比当前字符A大,那么A代表-1) 将字符串每个位置数字累加,得到字符串的和; 比如说: ABB = -1 + 10 + 10 = 19; BBA = 10 + 10 + 1 = 21;

现在可以修改字符串s中的一个字符,替换为A~E中的任意一个字符; 问,字符串的和最大为多少?

输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000) 每个样例一行,字符串𝑠(1≤|𝑠|≤2⋅105)

输出: 每个样例一行,输出修改后最大的字符串和;

Examples input 4 DAAABDCA AB ABCDEEDCBA DDDDAAADDABECD

output 11088 10010 31000 15886

题目解析: 还是从简单开始思考。 只有单个字母时,直接选择替换为E,收益为E与当前字母的差距; 当有两个字母时,就需要考虑特殊情况,正常AB这样的组合,还是会选择替换成EB;但是当BA这样的组合时,继续选A就会导致B变成负数,此时除了正收益,还有额外的负收益; 那么就需要统一计算,负收益也比较容易计算:替换后,所在位置前,原来ABCD字母价值为正的部分;(注意,如果原来就为负,没有负收益) 这样从左到右枚举整个数组即可得到最优解。

但是,自己还是犯了一个错误:主观判断,无法证明。 在分析样例的时候,还是太过急,从两个字母直接推出来最优解,情况还是不够丰富。 因为修改字母除了修改为最大,还可以修改为较小值。 这里既然要枚举每个位置的值,是不是也可以考虑枚举每个位置修改为A/B/C/D/E的值? 这样可以解决DDDDDDDDDDDDDDE这样的case,我们可以修改为DDDDDDDDDDDDDDD。

所以,更严谨的做法,我们应该枚举每一个位置分别对应修改为A/B/C/D/E的情况,但是这样的复杂度是O(N^2),明显超出题目限制; 但是分析其中冗余的部分,由贪心思想我们可以发现,假如一个字符D要修改,要么就是第一个D,要么是最后一个D; 为什么呢? 由最开始的替换为E的思路,这是从博取正收益的角度去出发,在这种情况下,假设要修改的是字符C,那么越往左的字符C,整体收益是越大的; 假如是我们从减少负收益的角度去出发,假设我们要修改是字符E,那么越往右的字符E,整体收益是越大的; 所以我们只要最开始出现和最后出现ABCDE位置,一共10个位置,每个位置再枚举修改为A/B/C/D/E的最大收益即可。

代码语言:javascript
复制
class Solution {
    static const int N = 201010;
    lld a[N];
    char s[N];
    lld val[5] = {1, 10, 100, 1000, 10000};
    
    lld getSum(int n) {
        lld ret = 0;
        char cur = 0;
        for (int i = n; i > 0; --i) {
            a[i] = val[s[i] - 'A'];
            if (s[i] < cur) a[i] *= -1;
            cur = max(cur, s[i]);
            ret += a[i];
        }
        return ret;
    }
    
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            scanf("%s", s+1);
            int n = strlen(s+1);
            int posFirst[5] = {0}, posLast[5] = {0};
            for (int i = 1; i <= n; ++i) {
                int idx = s[i] - 'A';
                if (!posFirst[idx]) posFirst[idx] = i;
                posLast[idx] = i;
            }
            lld ans = -0xfffffffffff;
            for (int i = 0; i < 5; ++i) {
                for (int j = 0; j < 5; ++j) {
                    if (!posFirst[i]) continue;;
                    char c = 'A' + j;
                    char tmp = s[posFirst[i]];
                    s[posFirst[i]] = c;
                    ans = max(ans, getSum(n));
                    s[posFirst[i]] = tmp;
                }
            }
            
            for (int i = 0; i < 5; ++i) {
                for (int j = 0; j < 5; ++j) {
                    if (!posLast[i]) continue;;
                    char c = 'A' + j;
                    char tmp = s[posLast[i]];
                    s[posLast[i]] = c;
                    ans = max(ans, getSum(n));
                    s[posLast[i]] = tmp;
                }
            }
            
            cout << ans << endl;
        }
    }
}
ac;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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