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

题目1

题目链接
题目大意:
有n个人参加投票,小明是第一个;
投票一共k轮,每轮每个人会做出一个选择,分别用+和-表示,那么一共有三个结果:
+的人数大于-的人数,那么-的人出局;
-的人数大于+的人数,那么+的人出局;
如果+和-的人数一样多,那么所有人出局;
出局的人,不再参与后续投票。

小明有特权,可以在第一轮投票之前淘汰掉若干个人,现在想知道,最终能有多少个人留到了最后;(小明一定保证自己会留到最后)

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤1000)
每个样例n+1行
第一行,整数𝑛 和 𝑘 (1≤𝑛,𝑘≤100)
接下来n行,每行有k个字符,表示每个人都投票结果。

输出:
输出留到最后的人数。

Examples
input

 5
 2 2
 ++
 +-
 1 3
 +-+
 4 1
 +
 -
 -
 +
 5 4
 ++++
 +--+
 ++-+
 +-++
 ++++
 4 2
 ++
 --
 --
 -+

output
1
1
2
2
1

题目解析:
由于题目不需要选择淘汰的顺序,并且小明一定会留在最后,那么和小明相同的投票结果的人会保留;

class Solution {
    static const int N = 201010;
    char s[N], r[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            cin >> n >> k;
            int ans = 1;
            cin >> s;
            for (int i = 1; i < n; ++i) {
                cin >> r;
                int tmp = 0;
                for (int j = 0; j < k; ++j) tmp += s[j] == r[j];
                ans += tmp == k;
            }
            cout << ans << endl;
        }
    }
}

题目2

题目链接
题目大意:
给出一个由整数和?组成的字符串,其中符号?可以匹配任何单个数字;
For example:
42 matches 4?;
1337 matches ????;
1337 matches 1?3?;
1337 matches 1337;
3 does not match ??;
8 does not match ???8;
1337 does not match 1?7.

现在问给出的字符串,最多存在多少个合法的匹配数字;(不包括前导零,整数大于零)

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

输出:
每个样例一行,输出最多存在多少个合法的匹配数字;

Examples
input
8
??
?
0
9
03
1??7
?5?
9??99

output
90
9
0
1
0
100
90
100

题目解析:
分情况讨论:
1、给出的字符串就存在前导零,那么结果为0;
2、给出的字符串合法,并且存在x个?号,那么有两种情况:
2.1,?号之前已经有整数,此时?号可以取任意数字,那么x个?号可以得到10^x个整数;
2.2,?号前面没有整数,此时第一个?号只能取1-9数字,所以会减少10^(x-1)个答案;


class Solution {
   static const int N = 201010;
   char s[N];
public:
   void solve() {
       int t;
       cin >> t;
       while (t--) {
           cin >> s;
           int n = strlen(s);
           int x = 0;
           for (int i = 0; i < n; ++i) x += (s[i] == '?');
       
           if (s[0] == '0') cout << 0 << endl;
           else if (s[0] == '?') {
               if (!x) cout << 1 << endl;
               else cout << (int)pow(10, x) - (int)pow(10, x - 1) << endl;
           }
           else {
               cout << (int)pow(10, x) << endl;
           }
       }
   }
}
ac;

题目3

题目链接
题目大意:
给出一个由n个整数组成的数组a,在数组中选择区间[l, r],将区间内的整数按照非降序进行处理得到整数数组b,比如说:
整数数组a为[6,7,3,4,4,6,5],选择区间[2, 5]得到整数数组b为[6,3,4,4,7,6,5];(区间外的整数位置不变)

现在已知整数数组a和变化后的整数数组b,求区间最长可能为多少?

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
每个样例3行:
第一行整数n(2≤𝑛≤2⋅1e5)
第二行整数数组a
第二行整数数组b
注意:数组a和数组b至少有一个位置的元素不相同。

输出:
每个样例一行,输出区间[l, r],表示可以选择进行操作的最长区间,如果由多个答案,输出任意一个;(题目保证有解)

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

output
2 5
1 3
2 3

题目解析:
假如不考虑复杂度,那么应该枚举区间[x, y],然后计算每个区间的可行性,这样复杂度为枚举复杂度 * 验证复杂度,枚举就已经超过题目限制;
注意题目给出的条件,数组a和b有元素不相同,那么至少存在2个位置不相同,否则题目无解;
假定第一个不相同元素的位置是x,最后一个不相同元素的位置是y,那么区间[x, y]必然是一个解,但不是最长解:
假设区间[x, y]右边的元素比区间[x, y]内的元素更大,那么可以纳入到这个区间内,因为排序完无影响;
同理对于区间[x, y]左边的元素,只要小于的等于区间内最小值,那么同意可以纳入到区间中;

注意:
区间外的整数,不一定有序的,比如说:
2 2 4 3 5 4
2 2 3 4 5 4

class Solution {
    static const int N = 201010;
    int a[N];
    int b[N];
public:
    void solve() {
        int t;
        cin >> t;
        int cnt = 0;
        while (t--) {
            int n, x, y = 0;
            cnt++;
            cin >> n;
            for (int i = 0; i < n; ++i) cin >> a[i];
            for (int i = 0; i < n; ++i) cin >> b[i];
            x = -1;
            for (int i = 0; i < n; ++i) {
                if (a[i] != b[i]) {
                    if (x == -1) x = i;
                    else y = i;
                }
            }
            
            int valMin = b[x], valMax = b[y];
            while (x > 0) {
                if (b[x - 1] <= valMin) --x;
                else break;
                valMin = min(valMin, b[x]);
            }
            while (y < n - 1) {
                if (b[y + 1] >= valMax) ++y;
                else break;
                valMax = max(valMax, b[y]);
            }

            cout << (x + 1) << " " << (y + 1) << endl;
            
        }
    }
}

题目4

题目链接
题目大意:
给出一个小写字母组成的字符串s,现在可以对字符串进行多次操作:
选择若干个不相邻的位置,移除这些位置上的字符,剩下的字符保持相对顺序组成新的字符串s;

假如操作若干次后,剩下的字符串都由相同字符组成,最少需要多少次;

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
每个样例一行,字符串s;(长度不超过200,000)

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

Examples
input
5
abacaba
codeforces
oooooooo
abcdef
mewheniseearulhiiarul

output
1
3
0
2
4

12345678

题目解析:
先简化题目,按照题目去掉若干个字符:
去掉字符长度为2,需要2个操作;
去掉字符长度为3,需要2个操作;
去掉字符长度为4,需要3个操作;
去掉字符长度为7,需要3个操作;
去掉字符长度为8,需要4个操作;
...
去除的规则比较简单,每次去除所有奇数位置,就可以最快去除;

题目的要求,最后只剩一种字符,那么结果一共有26种组合。
以字符a为例,遍历一遍字符串,那么就可以得到若干个由a分隔的区间,其中最长的区间假设是x,那么这个区间的移除代价就是最终的移除代价;
同理得到26个字母的结果,取其最小得到结果。

class Solution {
    static const int N = 201010;
    char s[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> s;
            int n = strlen(s);
            int ans = n;
            for (int i = 0; i < 26; ++i) {
                char c = 'a' + i;
                int len = 0, last = -1;
                for (int j = 0; j < n; ++j) {
                    if (s[j] == c) {
                        last = j;
                    }
                    else {
                        len = max(len, j - last);
                    }
                }
                if (len == 0) {
                    ans = 0;
                    break;
                }
                int tmp = 1;
                while ((1 << tmp) <= len) ++tmp;
                ans = min(ans, tmp);
            }
            cout << ans << endl;
        }
    }
}

题目5

题目链接
题目大意:
有一排格子排成一行,编号从左到右分别为0、1、2、3;
小明站在第0个格子,每次小明有三个选择可以进行操作:
1、移动到下一个格子:从格子x移动到格子x+1;
2、按下shift按钮;
3、松开shift按钮,小明在按下shift按钮期间经过的格子会被染色;

现在只有若干个区间[x, y]允许染色,区间外的格子不允许染色;
现在想要染色k个格子,问最少需要操作多少次;

1e18

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
每个样例3行:
第一行,整数 𝑛 和 𝑘 (1≤𝑛≤2⋅1e5; 1≤𝑘≤1e9)
第二行,整数 𝑙1,𝑙2,…,𝑙𝑛,表示n个区间的起点 (1≤𝑙1<𝑙2<⋯<𝑙𝑛≤1e9)
第三行,整数 𝑟1,𝑟2,…,𝑟𝑛 ,表示n个区间的终点 (1≤𝑟𝑖≤109; 𝑙𝑖≤𝑟𝑖<𝑙𝑖+1−1)
n个区间没有重合;

输出:
每个样例一行,输出最少的操作次数,如果无解则输出-1;

Examples
input
4
2 3
1 3
1 4
4 20
10 13 16 19
11 14 17 20
2 3
1 3
1 10
2 4
99 999999999
100 1000000000

output
8
-1
7
1000000004

题目解析:
简单策略,小明从左到右,只要经过允许的区间就染色,直到k个格子;
考虑到要最小操作次数,那么同一个区间的染色操作要合并,那么策略可以进行优化:
对于每一个区间,先考虑区间是否小于需要染色数量,是的话则直接染色整个区间;
如果当前区间足够剩余染色数量,则选择需要染色的x个格子即可。
但是这种策略少考虑了一种情况:
以题目样例3为例,假设一种情况是1011111111,其实先选择前面的1,则会花费3的代价(两次shift+1次移动),总的花费是8;如果不选择前面1,而是选择后面位置3开始的1,则只需要花费的代价是7;
为什么会出现这种情况?
因为前面会有2次选中操作,但是后面则只需要1次选中操作,减少了1次选中操作(即是2次shift),虽然多花费了1次move操作,但是总的花费还是减少了1;
所以在这种情况下,简单的策略在以下这种情况:
101010....011111....0110..101010....1111111... 都会难以处理,因为在决策是否要染色时,还可能受到后续方块的影响。

为了简化思考过程,可以我们把移动和选择拆分来看,先假设小明从0开始移动到第i个可以染色区间,小明经过的区间可以分为三类:
1、长度为1区间;
2、第1个到第i-1个区间中,长度大于1的区间;
3、第i个区间;(为什么第i个单独列出来,是因为第i个允许仅染色部分)

移动的代价分为两部分,首先是移动到第i个区间起始位置,另外一个是在第i个区间移动的距离;
选择的代价,首先是长度大于>1的区间,然后是第i个区间,接着是长度为1的区间;

Example:
k=10
10101010101010101010111011111111111111
19+2*10=39
20+4+7+4=35


class Solution {
    static const int N = 201010;
    int a[N], b[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            cin >> n >> k;
            for (int i = 0; i < n; ++i) cin >> a[i];
            for (int i = 0; i < n; ++i) cin >> b[i];
            lld sum = 0, oneCnt = 0;
            lld ans = 0xffffffffffff;
            for (int i = 0; i < n; ++i) {
                lld len = b[i] - a[i] + 1;
                if ((sum + len) >= k && k >= (sum - oneCnt)) {
                    if (sum - oneCnt + len >= k) {
                        ans = min(ans, a[i] - 1 + (i - oneCnt + 1) * 2 + (k - (sum - oneCnt)));
                    }
                    else {
                        ans = min(ans, a[i] - 1 + (i - oneCnt + 1) * 2 + len + 2 * (k - (sum - oneCnt + len)));
                    }
                }
                if (len == 1) ++oneCnt;
                sum += len;
            }
            if (ans == 0xffffffffffff) cout << "-1" << endl;
            else cout << ans << endl;
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容