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

2023-04-01  本文已影响0人  落影loyinglin

题目1

题目链接
题目大意:
给出n个整数,求移除掉最少的整数,要求:
剩下的整数中,任意两个相邻整数之和都是偶数;

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

输出:
每个样例一行,输出最少的移除整数。

Examples
input
2
5
2 4 3 6 8
6
3 5 9 7 1 3
output
1
0

题目解析:
奇+奇=偶;
偶+偶=偶;
奇偶和偶奇都是奇数;
所以剩下的整数中,要么全部是整数,要么全部是奇数;

枚举下这两种情况即可。

class Solution {
    static const int N = 201010;
    int a[N];

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            int x = 0, y = 0;
            while (n--) {
                int k;
                cin >> k;
                x += k % 2;
                y += (k + 1) % 2;
            }
            cout << min(x, y) << endl;
        }
    }
}
ac;

题目2

题目链接
题目大意:
给出n个整数的数组a,a[i]<=a[i+1];
现在需要调整这些整数的顺序,得到新的数组b;
要求b[i]>=a[i];

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

输出:
每个样例一行,如果有解则输出调换后的顺序;(不能和原来一样)
如果无解则输出-1;

Examples
input
2
5
1 1 1 1 1
6
3 6 8 13 15 21

output
5 1 2 3 4
-1

题目解析:
由于调换之后,要保持新的数字大于旧的数字,那么可以推出只能换相同的数字,因为假如换过来一个更大的数字,那么这个数字所在的位置就无法得到一个更大的数字;

于是,我们只需要遍历数组,遇到不同的数字,则检查下前面不同整数的情况,如果大于1个,则挪一个位置即可;

class Solution {
    static const int N = 201010;
    int a[N];
    int c[N];

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            vector<int> vec;
            for (int i = 0; i < n; ++i) {
                scanf("%d", &a[i]);
            }

            int left = 0, right = 0;
            int ans = 1;

            for (int i = 1; i <= n; ++i) {
                if ((i == n) || (a[i] != a[i - 1])) {
                    right = i;
                    // [left, right)
                    if (right - left <= 1) {
                        ans = 0;
                        break;
                    }
                    for (int j = left; j < right - 1; ++j) {
                        c[j] = j + 1;
                    }
                    c[right - 1] = left;
                    left = i;
                }
            }
            if (ans) {
                for (int i = 0; i < n; ++i) {
                    cout << c[i] + 1 << " ";
                }
                cout << endl;
            }
            else {
                cout << -1 << endl;
            }
        }
    }
}
ac;

题目3

题目链接
题目大意:
给出一个长度为n的二进制字符串s,现在定义一个函数f(s)为数字中的所有两两相邻数字组成的十进制数字之和,比如说:
𝑠=1011 时,可以得到10、01、11,那么𝑓(𝑠)=10+01+11=22;

现在最多可以执行k次操作,每次操作可以交换一次相邻数字,问f(s)最小值是多少;

输入:
第一行,整数𝑡 表示t个样例𝑡 (1≤𝑡≤1e5)
每个样例俩行,第一行 整数 𝑛 and 𝑘 (2≤𝑛≤1e5, 0≤𝑘≤1e9)
第二行长度为n的字符串s;

输出:
每个样例一行,输出最小的f(s);

Examples
input
3
4 0
1010
7 1
0010100
5 2
00110
output
21
22
12

题目解析:
根据f(s)的定义,我们知道大部分数字都会被使用两次,一次是在十位数,一次是在个位数;第一个数字只会用在十位数,最后一个数字只会用在个位数;
那么如果想要令f(s)尽可能小,就是要将数字尽可能放在头尾两边;

考虑数字1可能会在出现在多个位置,我们用left和right来表示数字1最左边和最右边的位置;(最右边不包括第n个位置)
假如剩余的交换次数,足够right位置的1交换到最右边,则交换;
假如剩余的交换次数,足够left位置的1交换到最左边,则交换;

然后再遍历整个数组,按照规则计算得到f(s)即可。

class Solution {
    static const int N = 201010;
    string s;

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            cin >> n >> k;
            cin >> s;
            int left = n, right = 0, sum = 0;
            int ans = 0;
            for (int i = 0; i < (n - 1); ++i) {
                if (s[i] == '1') {
                    ++sum;
                    left = min(left, i);
                    right = i;
                }
            }
            if (sum > 0 && s[n - 1] == '0') {
                if (k >= n - 1 - right) {
                    --sum;
                    k -= n - 1 - right;
                    s[right] = '0';
                    s[n - 1] = '1';
                }
            }
            if (sum > 0 && k > 0 && s[0] == '0') {
                if (k >= left) {
                    --sum;
                    k -= left;
                    s[left] = '0';
                    s[0] = '1';
                }
                
            }
            for (int i = 0; i < n - 1; ++i) {
                ans += (s[i] - '0') * 10 + (s[i + 1] - '0');
            }
            cout << ans << endl;
        }
    }
}
ac;

题目4

题目链接
题目大意:
给出在一个字符串s,现在可以将字符串s复制一遍,然后重新排序字符顺序;
希望能得到一个回文串。

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

输出:
每个样例一行,输出满足最终的回文串。

Examples
input
4
a
sururu
errorgorn
anutforajaroftuna
output
aa
suurruurruus
rgnororerrerorongr
aannuuttffoorraajjaarrooffttuunnaa

题目解析:
一个回文串的要求是从左到右,从右到左 两次遍历的字符串是一样的。
那么对于给出的字符串s,我们只需要反着生产一遍即可,比如说:
abc,我们得到abc + cba= abccba

除了此办法,由于题目允许随意排序,那么遍历字符串,记住a-z的数量,然后左右两边再一起填字符也是可以的。

class Solution {
    static const int N = 201010;
    char str[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> str;
            int n = strlen(str);
            cout << str;
            for (int i = n - 1; i >= 0; --i) putchar(str[i]);
            cout << endl;
        }
    }
}
ac;

题目5

题目链接
题目大意:
给出整数n,现在需要找到n个整数𝑎1,𝑎2,…,𝑎𝑛 满足 1≤𝑎[𝑖]≤1e9,并且有:
𝑎1⊕𝑎2⊕⋯⊕𝑎𝑛=(𝑎1+𝑎2+⋯+𝑎𝑛)/𝑛。

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

输出:
每个样例一行,输出满足要求的n个整数。

Examples
input
3
1
4
3
output
69
13 2 8 1
7 7 7

题目解析:
先从n=1开始考虑,此时任意数字都满足;
n=2时,首先a1+a2必须是偶数,那么我们可以推测二进制尾部一定都是0或者1,假设a1+a2结果是4,那么有1和3符合要求;
n=3时,类似n=1,我们加入两个相同数字,1+1+1也满足;
n=4时,我们知道n=2时,有1+3满足要求,并且结果是2,那么可以加入2个2,得到1、3、2、2;
...

思考:
题目的重点在于关注到异或的特点,两个相同数字异或值为0;

class Solution {
    static const int N = 201010;
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            if (n % 2) {
                for (int i = 0; i < n; ++i) cout << 1 << " ";
                cout << endl;
            }
            else {
                cout << "1 3 ";
                for (int i = 0; i < n - 2; ++i) cout << 2 << " ";
                cout << endl;
            }
        }
    }
}
ac;
上一篇下一篇

猜你喜欢

热点阅读