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

正文

题目1

题目链接
题目大意:
有n个糖果,分给两个人A和B,要求:
两个人都有分配到糖果;
糖果不能拆分,必须全部分分完;
A的糖果数量比B的要多;

问,最终有多少种分配方案。

输入:
第一行,整数𝑡表示有t个样例数量 (1≤𝑡≤1000)
接下来每个样例一行,整数𝑛 (1≤𝑛≤2⋅1e9)

输出:
每个样例一行,输出存在分配方案,不存在则输出0;

Examples
input
6
7
1
2
3
2000000000
763243547
output
3
0
0
1
999999999
381621773

样例解释:
样例1:
7个糖果,有下面3个方案:
𝑎=6, 𝑏=1;
𝑎=5, 𝑏=2;
𝑎=4, 𝑏=3.

题目解析:
分糖条件写的很清楚,两个整数a和b,要求a<b;
对于数字n来说,如果n是偶数,那么有n/2-1种可能;
如果n是奇数,那么有n/2种可能;
利用计算机整除的特性,可以表述为(n-1)/2;


int main(int argc, const char * argv[]) {
    // insert code here...
    
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        cout << (n - 1) / 2 << endl;
    }   
    
    return 0;
}

题目2

题目链接
题目大意:
有三个正整数n,a,b,现在需要构造一个字符串s,长度为n,只包含小写字母,并且满足要求:
字符串s中,任何长度为a的子串要包含b个不同的字符。

输入:
第一行,整数𝑡表示有t个样例数量 (1≤𝑡≤2000)
接下来每个样例一行,四个整数 𝑛, 𝑎 and 𝑏 (1≤𝑎≤𝑛≤2000,1≤𝑏≤min(26,𝑎))

输出:
每个样例一行,输出满足要求的字符串;(题目保证答案一定存在)

Examples
input
4
7 5 3
6 1 1
6 6 1
5 2 2
output
tleelte
qwerty
vvvvvv
abcde

题目解析:
题目的要求是,长度为a的子串中,有b个不同的字符;
那么将b个字符构成的字符串不断重复,即可满足题目要求;
比如说题目样例 7 5 3
b=3,则用abc不断循环,得到abcabca
样例 5 2 2
b=2,则用ab不断循环,得到ababa

实现较为简单。


int main(int argc, const char * argv[]) {
    // insert code here...
    
    int t;
    cin >> t;
    while (t--) {
        int n, a, b;
        cin >> n >> a >> b;
        int k = 0;
        for (int i = 0; i < n; ++i) {
            putchar('a' + k);
            k = (k + 1) % b;
        }
        puts("");
    }   
    
    return 0;
}

题目3

题目链接
题目大意:
有n个整数a[i],现在需要从中选择两组数字,要求:
1、两组数字的数量一样,每个整数只能划分到一个组内;
2、第一组的数字各不相同,第二组的数字完全相同;
现在希望两组数字尽可能的多,问最多一组能有几个整数。

输入:
第一行,整数𝑡表示有t个样例数量 (1≤𝑡≤10000)
接下来每个样例两行,第一行整数𝑛 (1≤𝑛≤2⋅1e5)
第二行n个整数 𝑎1,𝑎2,…,𝑎𝑛 (1≤𝑎𝑖≤𝑛),

输出:
每个样例一行,整数x,表示一组最多能够有x个整数。

Examples
input
4
7
4 2 4 1 4 3 4
5
2 1 5 4 3
1
1
4
1 1 1 3
⁣output
⁣3
⁣1
⁣0
⁣2

题目解析:
假如没有要求2,那么直接平分,x最大值就是n/2;
单独考虑不同数字的情况,直接算出数组中有k个不同的整数q,再算出数组中最多重复的整数w;
大多数情况下,min(q, w)就是答案了。
但是存在q和w会公用一个整数,比如说1.2.3,3,3这种情况,或者1.2.3.4.5.2.2情况。

当w<=q-1的时候,重复的数字比较少,所以答案就是w;
如果w>q-1的时候,重复的数字比较多,那么优先把重复的数字分配到第一组,答案就是min(w-1,q);

int a[N];
map<int, int> hmap;

int main(int argc, const char * argv[]) {
    // insert code here...
    
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        hmap.clear();
        int maxCount = 0;
        for (int i = 0; i < n; ++i) {
            int k;
            cin >> k;
            ++hmap[k];
            maxCount = max(maxCount, hmap[k]);
        }
        
        if (hmap.size() <= maxCount - 1) {
            cout << hmap.size() << endl;
        }
        else {
            cout << min((int)hmap.size() - 1, maxCount) << endl;
        }
    }   
    
    return 0;
}

题目4

题目链接
题目大意:
长度为n的字符串,一共有三种字符,'B', 'R', '?';
'?'的字符需要填充为'B'或者'R';
现在小明不喜欢相连两个字符是一样的, 现在需要知道怎么填充使得最终相连两个字符相同的情况尽可能少?
比如说"BRRRBBR"就有3个相连的字符相同,"BB"出现一次,"RR"出现两次;

输入:
第一行,整数𝑡表示有t个样例数量 (1≤𝑡≤100)
接下来每个样例两行,第一行整数𝑛 (1≤𝑛≤100)
第二行长度为n的字符串s

输出:
每个样例一行,输出由'B'和'R'字符串构成的字符串。

Examples
input
5
7
?R???BR
7
???R???
1
?
1
B
10
?R??RB??B?

output
BRRBRBR
BRBRBRB
B
B
BRRBRBBRBR

题目解析:
方案1,动态规划,dp[i][2]表示前i个字符,第i个字符为B、R的最小重复次数;
初始化的时候,如果第1个字符是?则dp[1][0]=dp[1][1]=0;
第1个字符是B,则dp[1][0]=0,dp[1][1]=n;(n是极大值,表示dp[1][1]不可取)
第1个字符是R,则dp[1][1]=0,dp[1][0]=n;(n是极大值,表示dp[1][0]不可取)
状态转移的时候,dp[i]可以由dp[i-1]来进行计算;
如果a[i]==B,则dp[i][0] = min(dp[i-1][0]+1, dp[i-1][1]); dp[i][1]=n;
如果a[i]==R,则dp[i][1] = min(dp[i-1][1]+1, dp[i-1][0]); dp[i][1]=n;
这样看最终dp[n]的最小值即可。

方案2,找到第一个不为?的字符,从这个位置分别向左右开始填充,每次优先选择相邻字符不相同的方案;
??R??
RBRBR

方案3,通过数学直接计算;
从左到右,如果第i个字符串前面??没有确定字符,则这段?不会产生特殊字符;若??前面有确定字符k,则根据a[i]和a[k]以及(i-k)可以直接计算出来有多少个相同字符;(0或者1)

考虑到题目要输出结果,还是方案2比较简单。

思考🤔:
如果是要连续3个字符串不一样呢?

class Solution {
    static const int N = 200010;
    string str;
    char ans[N];

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            cin >> str;
            int pos = n;
            for (int i = 0; i < n; ++i) {
                if (str[i] != '?') {
                    pos = i;
                    break;
                }
            }
            ans[n] = '\0';
            if (pos == n) {
                for (int i = 0; i < n; ++i) {
                    ans[i] = i % 2 ? 'B' : 'R';
                }
            }
            else {
                ans[pos] = str[pos];
                for (int i = pos - 1; i >= 0; --i) {
                    if (str[i] == '?') {
                        ans[i] = 'B' + 'R' - ans[i + 1];
                    }
                    else {
                        ans[i] = str[i];
                    }
                }
                for (int i = pos + 1; i < n; ++i) {
                    if (str[i] == '?') {
                        ans[i] = 'B' + 'R' - ans[i - 1];
                    }
                    else {
                        ans[i] = str[i];
                    }
                }
            }
            printf("%s\n", ans);
        }
    }
}
ac;

题目5

题目链接
题目大意:
从n个整数的数组中,找到(i, j) 要求 l ≤ a[i]+a[j] ≤ r,问有多少(i, j)符合要求;

输入:
第一行是整数t,表示有t个样例 (1≤𝑡≤10000 ).
每个样例第一行是整数𝑛,𝑙,𝑟 (1≤𝑛≤2⋅1e5, 1≤𝑙≤𝑟≤1e9)
第二行是n个整数𝑎1,𝑎2,…,𝑎𝑛 (1≤𝑎𝑖≤109).

输出:
(𝑖,𝑗)的数量,要求是 (𝑖<𝑗) 并且 𝑙≤𝑎𝑖+𝑎𝑗≤𝑟.

Examples
input
4
3 4 7
5 1 2
5 5 8
5 1 2 4 3
4 100 1000
1 1 1 1
5 9 13
2 5 5 1 1

output
yes
2
7
0
1

题目解析:
题目要求的是任意a[i]和a[j],那么数组的顺序没有意义,可以直接将数组进行排序;
如果不考虑复杂度,我们可以枚举pair(i, j)是否满足要求,这样复杂度是N*N;
由于排序完之后,数组是有序的,我们在枚举pair(i, j)的时候,可以采用下面的策略:
从小到大枚举i,假设已经先取了数字a[i]并且i<j,要求是找到l<=a[i]+a[j]<=r,那么就是在区间[i+1, n]里面找到l-a[i]作为起点,r-a[i]作为终点的区间;
我们可以采用二分查找来,也可以使用快捷方法lower_bound。


class Solution {
    static const int N = 200010;
public:
    int a[N];

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, l, r;
            cin >> n >> l >> r;
            for (int i = 0; i < n; ++i) {
                cin >> a[i];
            }
            sort(a, a + n);
            lld sum = 0;
            for (int i = 0; i + 1 < n; ++i) {
                int left = l - a[i];
                int right = r - a[i] + 1;
                // 从i+1开始,找到第一个大于等于left的数字作为起点x
                int x = lower_bound(a + i + 1, a + n, left) - a;
                if (x >= n) {
                    continue;;
                }
                // 新x开始,找到第一个大于right的数字作为终点y
                int y = lower_bound(a + x, a + n, right) - a;
                sum += y - x;
            }
            cout << sum << endl;
        }
    }
}
ac;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容