前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >程序员进阶之算法练习(七十八)

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

作者头像
落影
发布2023-07-09 15:26:23
发布2023-07-09 15:26:23
16300
代码可运行
举报
文章被收录于专栏:落影的专栏落影的专栏
运行总次数:0
代码可运行

题目1

题目链接 题目大意: 在二维坐标系中,每次有两个走法: 1、从(𝑥,𝑦) 到 (𝑥+1, 𝑦+1); 2、从(𝑥,𝑦) 到 (𝑥-1, 𝑦);

现在有初始点坐标(a, b),想要到达坐标(c, d),想知道最少的步数。

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

输出: 每个样例一行,输出最小的步数;如果不存在则输出-1;

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

output 4 6 -1 0 3 3

题目解析: 由条件知道,y只能+1或者保持不动,那么先计算y坐标的变化,得到y移动的位置; 剩下的位置只能走x-1的步数,如果x过小则误解,否则x坐标的差距就是剩余步数。

代码语言:javascript
代码运行次数:0
运行
复制
class Solution {
    static const int N = 201010;
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int a, b, c, d;
            cin >> a >> b >> c >> d;
            int ok = 0, ans = 0;
            do {
                if (b > d) break;
                ans += d - b;
                a += d - b;
                if (a < c) break;
                ans += a - c;
                ok = 1;
            } while (0);
            cout << (ok ? ans : -1) << endl;
        }
    }
}

题目2

题目链接 题目大意: n张桌子排成一行,现在Alice开始分配桌子: 1、每次分配的桌子数量依次递增,从1张、2张、3张、、n张,开始分配,直到分配结束;(假如最后一次不足n张,也会分配) 2、第1次分配Alice,然后接下来2次分给Bob,2次分给Alice,不断循环;

问最终Alice和Bob各自能分配到多少张桌子;

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

输出: 每个样例一行,分别输出Alice和bob能分配到的桌子数量。

Examples input 5 10 6 17 8 1000000

output 5 5 1 5 10 7 3 5 500202 499798

题目解析: 最简单的做法,遍历数组然后不断记住当前是第几次分配和桌子归属,O(N)可以得到Alice和Bob的桌子数量; 这里面我们尝试用更快的方法来求解。 按照题目每次分配桌子规则的要求,我们假设可以分配k次,则有(1+2+3+...+k)=n+z,其中z是一个补偿值,因为最终第k次可能不足,我们用z将其补足; 我们知道前k项和是 (1+k) * k / 2,通过n我们可以快速计算出来k的大小,以及第k次分配的真实数量; 我们令k=sqrt(2 * n),然后再累加k,直到(1+k)*k/2大于等于n即可;(这个时间复杂度是O(1)) 当我们知道k之后,就可以按照要求,枚举1、2、3、、、k的归属,这样做法的时间复杂度是根号N,即是O(sqrt(N) ); 但是我们还想追求更快的速度: 我们知道,Alice分得1之后,Bob和Alice会分别获得2个数字; 这样Alice可以得到公式Alice=(0+1) + (4+5) + (8+9) + ...; Bob可以得到公式Bob=(2+3) + (6+7) + (10+11) + ... ; 我们用int(k/4)可以得到最终公式的项数,剩下的不足4个数字,则可以再手动分配,这样的复杂度是O(1),整个题目的复杂度可以降为1;

代码语言:javascript
代码运行次数: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 k = sqrt(n);
            while ((1 + k) * k / 2 < n) {
                ++k;
            }
            int diffZ = (k + 1) * k / 2 - n; // 最后一个不够的数量
            
            lld ansA = 0, ansB = 0;
            int cntA = (k - 1) / 4 + 1;
            ansA = (0 + 1 + (cntA - 1) * 4 + (cntA - 1) * 4 + 1) * cntA / 2;
            int leftA = k - ((cntA - 1) * 4 + 1);
            if (leftA >= 3) ansA += cntA * 4;
            
            int cntB = (k + 1) / 4;
            ansB = (2 + 3 + (cntB - 1) * 4 + 2 + (cntB - 1) * 4 + 3) * cntB / 2;
            int leftB = k - (cntB * 4 - 1);
            if (leftB >= 3) ansB += cntB * 4 + 2;
            
            if (k % 4 == 0 || k % 4 == 1) ansA -= diffZ;
            else ansB -= diffZ;
            
            cout << ansA << " " << ansB << endl;
        }
    }
}

题目3

题目链接 题目大意: 有n个整数的数组,如果数组存在某个元素,等于该元素前面所有数字的和,则称这个元素为ugly; 比如说数组 [6,3,9,6],元素 9 = 6 + 3,则元素9是ugly的; 现在可以任意调换数组中元素的位置,但是不能删除或者增加元素,问是否能够让数组没有ugly的元素。 输入: 第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤2000) 每个样例2行,第一行整数𝑛 (2≤𝑛≤50) 第二行n个整数 𝑎1,𝑎2,…,𝑎𝑛 (1≤𝑎1≤𝑎2≤⋯≤𝑎𝑛≤100)

输出: 每个样例一行,如果无解输出NO,如果有解则输出YES,接下来一行输出调整顺序后的整数数组;

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

output YES 3 6 3 6 NO YES 2 4 1 5 3 YES 1 4 4

题目解析: ugly的要求是前面元素的和,那么前面的元素如果比当前元素大,必然前面元素和会更大; 将整数数组按照从大到小排队,由于整数元素都有>=1,那么从第3个元素开始,必然不会出现ugly元素; 假如a1=a2,那么将最小的元素插入到a2中。(如果数组中所有元素相同,则无解)

代码语言:javascript
代码运行次数:0
运行
复制
class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            for (int i = 0; i < n; ++i) cin >> a[i];
            sort(a, a + n, greater<int>());
            if (a[0] == a[n - 1]) cout << "NO" << endl;
            else {
                cout << "YES" << endl;
                int tmp = a[1];
                a[1] = a[n - 1];
                a[n - 1] = tmp;
                for (int i = 0; i < n; ++i) cout << a[i] << " ";
                cout << endl;
            }
        }
    }
}

题目4

题目链接 题目大意: 给出整数n,构造一个n x n的整数矩阵,要求: 1、数字是由1、2、3到n^2的整数组成; 2、相邻整数的绝对值差,能形成尽可能多的数字。

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

输出: 每个样例n行,每行n个整数;

Examples input 2 2 3

output 1 3 4 2 1 3 4 9 2 7 5 8 6

样例解释: 对于n=2,矩阵中相邻整数的差有: |1−3|=2 , |1−4|=3 , |3−2|=1 和 |4−2|=2,一共有3个整数(1、2、3);

题目解析: 有题目的要求1可以知道,矩阵中差的绝对值,最大值为n^2 - 1,最小值为1,那么不同数最多可能有:1、2、3、、、n^2-1; 那么如何构造一个最大值?首先1放在左上角的位置,然后1的下面和右面放n^2和 n^2-1,这样就构造了两个较大的数字; 同理接下来就可以放再较小的整数。 以整数n=4为例 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 接着 1 15 0 0 16 0 0 0 0 0 0 0 0 0 0 0 然后是 1 15 4 0 16 3 0 0 2 0 0 0 0 0 0 0 最终就是大小交替的效果。 1 15 4 11 16 3 12 7 2 13 6 9 14 5 10 8

代码语言:javascript
代码运行次数:0
运行
复制
class Solution {
    static const int N = 101;
    int a[N][N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            int x = 1, y = n * n;
            for (int i = 0; i < n; ++i) {
                if (i % 2 == 0) {
                    for (int j = 0; i - j >= 0; ++j) { // a[i][0]
                        a[i - j][j] = x++;
                    }
                }
                else {
                    for (int j = 0; i - j >= 0; ++j) { // a[i][0]
                        a[i - j][j] = y--;
                    }
                }
            }
            for (int j = 1; j < n; ++j) {
                if ((n + j - 1) % 2 == 0) {
                    for (int i = n - 1; i - j >= 0; --i) { // a[n - i][j]
                        a[i][j + n - 1 - i] = x++;
                    }
                }
                else {
                    for (int i = n - 1; i - j >= 0; --i) { // a[n - i][j]
                        a[i][j + n - 1 - i] = y--;
                    }
                }
            }
            
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) cout << a[i][j] << " ";
                cout << endl;
            }
        }
    }
}

题目5

题目链接 题目大意: 小明和n个人参加比赛,已知n个人的序号分别为1、2、3、、、n; 当n个人中的两个人(序号i和j)内部比赛时,假如i < j,则序号j的选手获胜;(即n个人比赛,序号大者获胜); 当小明与n个人中的一个比赛时,对于选手i,小明需要花费a[i]时间进行准备,如果有充足时间准备则能胜出,否则会落败; 已知小明一共有m的时间进行比赛,同一时间只能准备一个选手; 最终名次就是胜场较多人数的+1,比如说胜场比小明多的人数一共有5人,那么小明就是第6名;(其他胜场和小明一样多的人,并列第6名) 问小明最的获胜名次最好是多少。(越低越好)

输入: 第一行,整数𝑡 表示t个样例 𝑡(1≤𝑡≤1e4) 每个样例2行,第一行整数 𝑛 and 𝑚 (1≤𝑛≤5⋅1e5) 第二行n个整数𝑎1,𝑎2,…,𝑎𝑛(0≤𝑎𝑖≤1000)

输出: 每个样例1行,输出小明最好的名次。

Examples input 5 4 401 100 100 200 1 3 2 1 2 3 5 0 1 1 1 1 1 4 0 0 1 1 1 4 4 1 2 2 1

output 1 2 6 4 1

题目解析: 如果时间比较充裕,那么足够准备所有人的比赛; 当时间不够时,通常的选择是优先准备耗时较少的选手;但是在这个题目中,会产生额外的影响: 如果我们没有针对某个选手准备,则该选手都胜场会+1,从而影响到自己的名次。 所以决策是否针对某个选手准备时候,要考虑当前的因素。

由此,我们产生一个简单的策略: 先将准备时间从小到大排序,然后我们从耗费时间小的开始准备,直到某个选手的准备时间不够; 此时我们先不要花时间准备,而是用剩下的时间,将所有能够准备的选手筛出,选择一个胜场数量等于小明胜场+1的选手进行准备;(如果不选他,他就会排在小明前面,选了他则并列)剩下的胜场数相同、胜场数少于小明的,并不会因为小明的选择而影响名次; 然后接下来的选手,由于我们无法准备,所有的选手胜场+1; 至此,我们得到一个解,胜场x,以及每个选手的胜场,得到最终的名次。

代码语言:javascript
代码运行次数:0
运行
复制
class Solution {
    static const int N = 601000;
    pair<int, int> a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n, m;
            cin >> n >> m;
            for (int i = 0; i < n; ++i) {
                cin >> a[i].first;
                a[i].second = i;
            }
            sort(a, a + n, lycmp);
            if (a[0].first > m) {
                cout << n + 1 << endl;
                continue;;
            }
            int index = -1, win = 0;
            while (index < n) {
                if (a[index + 1].first <= m) {
                    m -= a[index + 1].first;
                    ++win;
                    ++index;
                }
                else {
                    break;
                }
            }
            
            for (int i = index; i < n; ++i) {
                if (a[index].first + m >= a[i].first && a[i].second == win) {
                    a[i].second -= 1;
                    break;
                }
            }
            int ans = 1;
            for (int i = 0; i < n; ++i) {
                if (i < index && a[i].second > win) ++ans;
                else if (i >= index && a[i].second >= win) ++ans;
            }
            cout << ans << endl;
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目1
  • 题目2
  • 题目3
  • 题目4
  • 题目5
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档