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

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

作者: 落影loyinglin | 来源:发表于2023-02-18 07:47 被阅读0次

    题目1

    题目链接
    题目大意:
    给出一个整数n,现在可以对整数执行一个操作:
    选择整数上两个不同位数的数字交换位置,然后移除整数最右边一位的数字;
    重复这个过程,直到整数只剩下1位;
    现在想知道这个剩下的数字最小可能为多少。

    输入:
    第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
    每个样例俩行,第一行 整数 𝑛 (10≤𝑛≤1e9)

    输出:
    每个样例一行,剩下最小的数字;

    Examples
    input
    3
    12
    132
    487456398
    output
    2
    1
    3

    题目解析:
    右边第二位是必胜位,每次都选择其他位数的数字进行交换,在只剩下2位数字的时候,将第一位和第二位交换,然后会去掉右边的数字,剩下原来右边第二位的数字;
    也就是说,当时数字位数大于等于3的时候,可以选择整数上最小的数字,将这个数字放在右边第二位,再采用上面的策略必然可以留下来;
    当数字位数只有2位时,留下来的数字必然是最右边的数字;

    class Solution {
        static const int N = 201010;
        int a[N];
    
    public:
        void solve() {
            int t;
            cin >> t;
            while (t--) {
                int n;
                cin >> n;
                if (n < 99) {
                    cout << n % 10 << endl;
                }
                else {
                    int ans = 9;
                    while (n) {
                        ans = min(ans, n % 10);
                        n /= 10;
                    }
                    cout << ans << endl;
                }
            }
        }
    }
    ac;
    

    题目2

    题目链接
    题目大意:
    给出整数a,b,c,满足a<b<c;
    构造三个整数x,y,z,满足:
    x mod y = a;
    y mod z = b;
    z mod x = c;

    输入:
    第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
    每个样例一行,𝑎 , 𝑏, 𝑐 (1≤𝑎<𝑏<𝑐≤1e8).

    输出:
    每个样例一行,输出满足的𝑥 , 𝑦, 𝑧 (1≤𝑥,𝑦,𝑧≤1e18)

    Examples
    input
    4
    1 3 4
    127 234 421
    2 7 8
    59 94 388
    output
    12 11 4
    1063 234 1484
    25 23 8
    2221 94 2609

    题目解析:
    题目要求里面并没有包括x、y、z的大小关系,那么如果要满足x%y = a,最简单的做法就是x=a,并且满足x<y;
    同理,可以得到y%z=b,会有y=b,并且满足y<z;
    但是在z%x=c,假如我们令z=c,满足z<x则会出现异常,因为由前面两个已经可以递推得到x<y<z;并且由于a<c,x如果为a是无法得到c的;

    由于a<c的约束,我们先考虑满足z%x=c的限制,即是令z=c,并且满足z<x;
    接着是y%z=b,可以令y=b,由于b<c=z,所以这个也能够满足;
    剩下的就是x%y=a,已知y=b,那么有x%b=a,即是x=b*k + a;

    此时要满足x=bk + a,z<x,可以令k=c,那么有x=cb+a;

    class Solution {
        static const int N = 201010;
    
    public:
        void solve() {
            int t;
            cin >> t;
            while (t--) {
                lld a, b, c;
                cin >> a >> b >> c;
                cout << (c * b + a) << " " << b << " " << c << endl;
            }
        }
    }
    ac;
    

    题目3

    题目链接
    题目大意:
    给出n x m的矩阵a,a[i][j]是一个整数;
    现在需要选择任意两列进行交换,现在想知道是否存在一种交换方式,满足:
    交换之后,每一行的元素,从左到右都是非递减的;

    输入:
    第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤100)
    每个样例第一行是整数𝑛 and 𝑚 (1≤𝑛,𝑚≤2⋅1e5)
    接下来会有n行m列的整数 𝑎[𝑖,𝑗] (1≤𝑎[𝑖,𝑗]≤1e9);

    输出:
    每个样例一行,输出满足的交换位置;(可以交换相同位置)
    如果无解则输出-1;

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

    output
    1 1
    -1
    1 2
    1 3
    1 1

    题目解析:
    先考虑一维的情况,给出一个数组,交换任意两个数字,然后将数组变成非递减;
    由于每次只能选择交换相同位置或者两个位置,也就是说要么影响2个数字,要么不影响,那么可以先计算一遍最长非递减的子序列得到长度k;
    根据k和n的区别,如果n-k>2,那么就是必然无解;
    但是由于k可以等于n、n-1、这几种情况是比较难以决策如何选择出来两个位置;
    考虑到只有一次交换(2个位置),那么假如将数组直接排序,是不是也可以通过这个来对比得到这个位置?
    假如排序之后,将有序数组和原数组对比,得到若干个不匹配的位置,如果不同数大于2则无解,如果不同数为2则就是交换的两个位置,如果不同数为0则不需要交换;(这里不存在不同数位1的情况)

    基于上面一维的分析,我们可以推导到二维数组的情况。
    当我们从上到下去遍历每一行元素时,当我们第一次找到存在2个位置不同的时候,我们就将这位置作为最终的交换位置;
    将整个矩阵相应列进行交换,最后判断结果是否符合要求。

    注意:如果某一行完全匹配(不同数为0),此时应该先忽略,去找不同位置为2的行;

    class Solution {
        static const int N = 201010;
        vector<int> a[N];
        vector<int> b[N];
    
    public:
        void solve() {
            int t;
            cin >> t;
            while (t--) {
                int n, m;
                cin >> n >> m;
                for (int i = 0; i < n; ++i) {
                    a[i].clear();
                    b[i].clear();
                    for (int j = 0; j < m; ++j) {
                        int x;
                        scanf("%d", &x);
                        a[i].push_back(x);
                        b[i].push_back(x);
                    }
                }
                vector<int> diff;
                bool ans = true;
                for (int i = 0; i < n; ++i) {
                    sort(a[i].begin(), a[i].end());
                    if (!diff.size()) {
                        for (int j = 0; j < m; ++j) {
                            if (a[i][j] != b[i][j]) {
                                diff.push_back(j);
                            }
                        }
                    }
                }
                if (diff.size() > 2) {
                    ans = false;
                }
                else if (!diff.size()){
                    diff.push_back(0);
                    diff.push_back(0);
                }
                else {
                    for (int i = 0; i < n; ++i) {
                        int x = diff[0], y = diff[1];
                        int tmp = b[i][x];
                        b[i][x] = b[i][y];
                        b[i][y] = tmp;
                        for (int j = 0; j < m; ++j) {
                            if (a[i][j] != b[i][j]) {
                                ans = false;
                            }
                        }
                    }
                    
                }
                
                if (ans) {
                    cout << diff[0] + 1 << " " << diff[1] + 1 << endl;
                }
                else {
                    cout << (-1) << endl;
                }
                
            }
        }
    }
    ac;
    

    题目4

    题目链接
    题目大意:
    在一个n x m的方格矩阵中,小明要从左上角(1,1)到右下角(n,m),小红要从左下角(n,1)到右上角(1,m);
    每个相邻格子的行动代价是1;
    小红先出发,沿路中每个经过格子都会放下一个传送器,这个不需要代价;
    小明晚出发,小明如果站在某个有传送器的格子,那么可以花费1代价,传送到另外一个有传送器的格子;
    问小红和小明到达目的,总的代价最小为多少;

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

    输出:
    每个样例一行,输出最小的总代价;

    Examples
    input
    7
    7 5
    5 7
    1 1
    100000 100000
    57 228
    1 5
    5 1
    output
    15
    15
    0
    299998
    340
    5
    5

    题目解析:
    在没有传送器的情况下,小红和小明的路径总代价都是n+m;
    假设小红不考虑小明,按照最短路径到达,总代价是n+m;然后小明借助小红的路径,理论上能节省的最大路径是max(n, m)-1,总的代价就是n+m+max(n, m)-1;
    此时路径应该就是小红先走到(1, 1),再走到(1, m);
    有没有可能小红为了让小明能够尽可能传送,特意绕路?不会,因为小红要走过的路才能让小明传送;所以一旦远离小红的最优路径n+m,所有给小明节省的代价,其实都是不划算的.
    注意:边界情况要考虑,n=1 和 m=1。

    class Solution {
        static const int N = 201010;
    
    public:
        void solve() {
            int t;
            cin >> t;
            while (t--) {
                int n, m;
                cin >> n >> m;
                if (n == 1 || m == 1) {
                    if (n == m) cout << 0 << endl;
                    else cout << max(n, m) << endl;
                }
                else cout << (n + m - 2) * 2 - max(n, m) + 2 << endl;
            }
        }
    }
    ac;
    

    题目5

    题目链接
    题目大意:
    给出包括n个非负整数的数组a,我们可以计算这个数组的beauty值:
    将数组每个元素除以k,向下取整得到这个元素的beauty值,数组的beauty值等于所有元素的beauty值总和;
    现在给出n、k、数组beauty值b和数组元素累加和s,求是否能够构造数组a;

    输入:
    第一行,整数𝑡 表示t个样例𝑡 (1≤𝑡≤1000)
    每个样例第一行 整数 𝑛, 𝑘, 𝑏, 𝑠 (1≤𝑛≤1e5, 1≤𝑘≤1e9, 0≤𝑏≤1e9, 0≤𝑠≤1e18).

    输出:
    每个样例一行,如果无解输出-1;如果有解则输出n个整数;𝑎1,𝑎2,…,𝑎𝑛 (0≤𝑎𝑖≤1e18)

    Examples
    input
    8
    1 6 3 100
    3 6 3 12
    3 6 3 19
    5 4 7 38
    5 4 7 80
    99978 1000000000 100000000 1000000000000000000
    1 1 0 0
    4 1000000000 1000000000 1000000000000000000
    output
    -1
    -1
    0 0 19
    0 3 3 3 29
    -1
    -1
    0
    0 0 0 1000000000000000000

    题目解析:
    为了方便思考,我们先考虑数组只有3个元素的情况,那么有:
    a1 + a2 + a3 = s ;
    a1/k + a2/k + a3/k = b;

    那么我们可以令a1=k * b,先保证条件b可以满足;
    因为除以k的原因,接下来a1到a3,都可以增大k-1的大小而且不影响b的值;
    那么s的上限就是k * b + n * (k - 1),下限就是k * b;
    满足这里的条件就有解。

    思考:
    如何证明正确性?假设a1/k + a2/k,a2需要大于k,那么将a2-k,并将a1+k是等价的;

    注意:
    超过int32,不过样例已经覆盖了这样的case。

    class Solution {
        static const int N = 201010;
    public:
        void solve() {
            lld t;
            cin >> t;
            while (t--) {
                lld n, k, b, s;
                cin >> n >> k >> b >> s;
                lld maxSum = k * b + n * (k - 1);
                if (maxSum < s || b * k > s) {
                    cout << -1 << endl;
                }
                else {
                    s -= b * k;
                    for (lld i = 0; i < n; ++i) {
                        lld tmp = min(k - 1, s);
                        s -= tmp;
                        if (i == 0) tmp += b*k;
                        cout << tmp << " ";
                    }
                    cout << endl;
                }
            }
        }
    }
    ac;
    

    相关文章

      网友评论

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

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