美文网首页
程序员进阶之算法练习(五十九)

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

作者: 落影loyinglin | 来源:发表于2022-03-13 17:21 被阅读0次

    正文

    题目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;
    
    

    相关文章

      网友评论

          本文标题:程序员进阶之算法练习(五十九)

          本文链接:https://www.haomeiwen.com/subject/rlfgcrtx.html