《算法竞赛进阶指南》0.8总结与练习(2)

122. 糖果传递

有n个小朋友坐成一圈,每人有a[i]个糖果。
每人只能给左右两人传递糖果。
每人每次传递一个糖果代价为1。
求使所有人获得均等糖果的最小代价。

输入格式
第一行输入一个正整数n,表示小朋友的个数。
接下来n行,每行一个整数a[i],表示第i个小朋友初始得到的糖果的颗数。

输出格式
输出一个整数,表示最小代价。

数据范围
1≤n≤1000000

输入样例:
4
1
2
5
4

输出样例:
4

#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;
const int N = 1000010;

int n;
LL a[N];

int main()
{
    scanf("%d", &n);
    LL sum = 0;
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        sum += a[i];
    }
    sum /= n; //中位数
    for(int i = n; i > 1; i--)
    {
        a[i] = sum - a[i] + a[i + 1];
    }
    a[1] = 0;
    
    sort(a + 1, a + n + 1);
    LL res = 0;
    
    for(int i = 1; i <= n; i++) res += abs(a[i] - a[(n + 1) / 2]); //上取整 n/2 的上取整 等价于 n+1 / 2 的下取整
    
    cout << res << endl;
    
    return 0;
}

123. 士兵

格格兰郡的N名士兵随机散落在全郡各地。
格格兰郡中的位置由一对(x,y)整数坐标表示。
士兵可以进行移动,每次移动,一名士兵可以向上,向下,向左或向右移动一个单位(因此,他的x或y坐标也将加1或减1)。
现在希望通过移动士兵,使得所有士兵彼此相邻的处于同一条水平线内,即所有士兵的y坐标相同并且x坐标相邻。
请你计算满足要求的情况下,所有士兵的总移动次数最少是多少。
需注意,两个或多个士兵不能占据同一个位置。

输入格式
第一行输入整数N,代表士兵的数量。
接下来的N行,每行输入两个整数x和y,分别代表一个士兵所在位置的x坐标和y坐标,第i行即为第i个士兵的坐标(x[i],y[i])。

输出格式
输出一个整数,代表所有士兵的总移动次数的最小值。

数据范围
1≤N≤10000,
−10000≤x[i],y[i]≤10000

输入样例:
5
1 2
2 2
1 3
3 -2
3 3

输出样例:
8

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 10010;

int n;
int x[N], y[N];

int work(int *q) // *q等价于q[0]
{
    int res = 0;
    sort(q, q + n);
    for(int i = 0; i < n; i++) res += abs(q[i] - q[i / 2]); //下标从0开始 i/2下取整
    return res;
}

int main()
{
    cin >> n;
    for(int i = 0; i < n; i++) cin >> x[i] >> y[i];
    
    sort(x, x + n);
    for(int i = 0; i < n; i++) x[i] -= i;
    
    cout << work(y) + work(x) << endl;
    
    return 0;
}

124. 数的进制转换

编写一个程序,可以实现将一个数字由一个进制转换为另一个进制。
这里有62个不同数位{0-9,A-Z,a-z}。

输入格式
第一行输入一个整数,代表接下来的行数。
接下来每一行都包含三个数字,首先是输入进制(十进制表示),然后是输出进制(十进制表示),最后是用输入进制表示的输入数字,数字之间用空格隔开。
输入进制和输出进制都在2到62的范围之内。
(在十进制下)A = 10,B = 11,…,Z = 35,a = 36,b = 37,…,z = 61 (0-9仍然表示0-9)。

输出格式
对于每一组进制转换,程序的输出都由三行构成。
第一行包含两个数字,首先是输入进制(十进制表示),然后是用输入进制表示的输入数字。
第二行包含两个数字,首先是输出进制(十进制表示),然后是用输出进制表示的输入数字。
第三行为空白行。
同一行内数字用空格隔开。

输入样例:
8
62 2 abcdefghiz
10 16 1234567890123456789012345678901234567890
16 35 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
35 23 333YMHOUE8JPLT7OX6K9FYCQ8A
23 49 946B9AA02MI37E3D3MMJ4G7BL2F05
49 61 1VbDkSIMJL3JjRgAdlUfcaWj
61 5 dl9MDSWqwHjDnToKcsWE1S
5 10 42104444441001414401221302402201233340311104212022133030

输出样例:
62 abcdefghiz
2 11011100000100010111110010010110011111001001100011010010001

10 1234567890123456789012345678901234567890
16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2

16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
35 333YMHOUE8JPLT7OX6K9FYCQ8A

35 333YMHOUE8JPLT7OX6K9FYCQ8A
23 946B9AA02MI37E3D3MMJ4G7BL2F05

23 946B9AA02MI37E3D3MMJ4G7BL2F05
49 1VbDkSIMJL3JjRgAdlUfcaWj

49 1VbDkSIMJL3JjRgAdlUfcaWj
61 dl9MDSWqwHjDnToKcsWE1S

61 dl9MDSWqwHjDnToKcsWE1S
5 42104444441001414401221302402201233340311104212022133030

5 42104444441001414401221302402201233340311104212022133030
10 1234567890123456789012345678901234567890

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
    int T;
    cin >> T;
    while(T --)
    {
        int a, b;
        string a_line, b_line;
        cin >> a >> b >> a_line;
        
        vector<int> number;
        for(auto c : a_line)
        {
            if(c >= '0' && c <= '9') number.push_back(c - '0');
            if(c >= 'A' && c <= 'Z') number.push_back(c - 'A' + 10);
            if(c >= 'a' && c <= 'z') number.push_back(c - 'a' + 36);
        }
        reverse(number.begin(), number.end()); // 从低位到高位存
        
        //短除法
        vector<int> res;
        while(number.size())
        {
            int r = 0; // 上一位的余数 进位 
            for(int i = number.size() - 1; i >= 0; i--)
            {
                number[i] += r * a; // 当前一位的值 = 上一位的借位 * 进制
                r = number[i] % b; // 余数 = 当前这一位 / b 
                number[i] /= b;
            }
            res.push_back(r); // 最后的余数就是转换进制的个位
            //把结果的0删除
            while(number.size() && number.back() == 0) number.pop_back();
        }
        
        reverse(res.begin(), res.end()); 
        for(auto x : res)
        {
            if(x <= 9) b_line += char(x + '0');
            if(x >= 10 && x <= 35) b_line += char(x - 10 + 'A');
            if(x >= 36) b_line += char(x - 36 + 'a');
        }
        
        cout << a << ' ' << a_line << endl;
        cout << b << ' ' << b_line << endl;
        cout << endl;
    }
    return 0;
}

125. 耍杂技的牛

农民约翰的N头奶牛(编号为1..N)计划逃跑并加入马戏团,为此它们决定练习表演杂技。
奶牛们不是非常有创意,只提出了一个杂技表演:
叠罗汉,表演时,奶牛们站在彼此的身上,形成一个高高的垂直堆叠。
奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。
这N头奶牛中的每一头都有着自己的重量Wi以及自己的强壮程度Si。
一头牛只撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。
您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。

输入格式
第一行输入整数N,表示奶牛数量。
接下来N行,每行输入两个整数,表示牛的重量和强壮程度,第i行表示第i头牛的重量Wi以及它的强壮程度Si。

输出格式
输出一个整数,表示最大风险值的最小可能值。

数据范围
1≤N≤50000,
1≤Wi≤10,000,
1≤Si≤1,000,000,000

输入样例:
3
10 3
2 5
3 3

输出样例:
2

#include <iostream>
#include <algorithm>
#include <limits.h>

using namespace std;

typedef pair<int, int> PII;

const int N = 50010;

int n;
PII cows[N];

int main()
{
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        int w, s;
        cin >> w >> s;
        cows[i] = {w + s, w}; //以 w + s 排序 pair 以第一个关键字排序
    }
    
    sort(cows, cows + n);
    
    int sum = 0, res = INT_MIN;
    for(int i = 0; i < n; i++)
    {
        int w = cows[i].second, s = cows[i].first - w;
        res = max(res, sum - s); //首先更新res
        sum += w;
    }
    cout << res << endl;
    return 0;
}

126. 最大的和

给定一个包含整数的二维矩阵,子矩形是位于整个阵列内的任何大小为1 * 1或更大的连续子阵列。
矩形的总和是该矩形中所有元素的总和。
在这个问题中,具有最大和的子矩形被称为最大子矩形。
例如,下列数组:

0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2

其最大子矩形为:

9 2
-4 1
-1 8

它拥有最大和15。

输入格式
输入中将包含一个N*N的整数数组。
第一行只输入一个整数N,表示方形二维数组的大小。
从第二行开始,输入由空格和换行符隔开的N2个整数,它们即为二维数组中的N2个元素,输入顺序从二维数组的第一行开始向下逐行输入,同一行数据从左向右逐个输入。
数组中的数字会保持在[-127,127]的范围内。

输出格式
输出一个整数,代表最大子矩形的总和。

数据范围
1≤N≤100

输入样例:
4
0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1

8 0 -2

输出样例:
15

#include <iostream>
#include <algorithm>
#include <limits.h>

using namespace std;

const int N = 110;

int n;
int g[N][N];

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) //求前缀和
        for(int j = 1; j <= n; j++)
        {
            cin >> g[i][j];
            g[i][j] += g[i - 1][j];
        }
        
    int res = INT_MIN;
    for(int i = 1; i <= n; i++) //枚举上界
        for(int j = i; j <= n; j++) //枚举下界 j从i开始
            {
                int last = 0;
                for(int k = 1; k <= n; k++)
                {
                    //last存以上一个数结尾的连续最大子矩阵的和,last < 0,负收益,则舍去,赋为0
                    last = max(last, 0) + g[j][k] - g[i - 1][k]; //以第k列为结尾的连续子矩阵最大和,上界为i,下界为j
                    res = max(res, last);
                    
                }
            }
    cout << res << endl;        
    return 0;        
}

127. 任务

今天某公司有M个任务需要完成。
每个任务都有相应的难度级别和完成任务所需时间。
第i个任务的难度级别为yi,完成任务所需时间为xi分钟。
如果公司完成此任务,他们将获得(500 * xi+ 2 * yi)美元收入。
该公司有N台机器,每台机器都有最长工作时间和级别。
如果任务所需时间超过机器的最长工作时间,则机器无法完成此任务。
如果任务难度级别超过机器的级别,则机器无法完成次任务。
每台机器一天内只能完成一项任务。
每个任务只能由一台机器完成。
请为他们设计一个任务分配方案,使得该公司能够最大化他们今天可以完成的任务数量。
如果有多种解决方案,他们希望选取赚取利润最高的那种。

输入格式
输入包含几个测试用例。
对于每个测试用例,第一行包含两个整数N和M,分别代表机器数量和任务数量。
接下来N行,每行包含两个整数xi,yi,分别代表机器最长工作时间和机器级别。
再接下来M行,每行包含两个整数xi,yi,分别代表完成任务所需时间和任务难度级别。

输出格式
对于每个测试用例,输出两个整数,代表公司今天可以完成的最大任务数以及他们将获得的收入。

数据范围
1≤N,M≤100000,
0<xi<1440,
0≤yi≤100

输入样例:
1 2
100 3
100 2
100 1

输出样例:
1 50004

/*
1.最大匹配当且仅当没有增广路经
2.权值最大匹配 带权最大二分图匹配有两种:1.带边权 KM算法完全匹配(没一点都匹配上)
                                        2.点权 贪心,匈牙利算法(按点权重从大到小排序,从大到小去做匈牙利算法 
                                        时间复杂度NM(N为点数,M为边数)
*/

#include <iostream>
#include <algorithm>
#include <set>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int N = 100010;

int n, m;
PII mchs[N], tasks[N];

int main()
{
    while(cin >> n >> m)
    {
        for(int i = 0; i < n; i++) cin >> mchs[i].first >> mchs[i].second;
        for(int i = 0; i < m; i++) cin >> tasks[i].first >> tasks[i].second;
        
        sort(mchs, mchs + n);
        sort(tasks, tasks + m); //从小到大排序
        
        multiset<int> ys; //可以用相同元素
        LL cnt = 0, res = 0;
        for(int i = m - 1, j = n - 1; i >= 0; i--) //从大到小遍历 i枚举任务,j枚举机器
        {
            int x = tasks[i].first, y = tasks[i].second;
            while(j >= 0 && mchs[j].first >= x) ys.insert(mchs[j --].second); //横坐标大于等于当前任务的机器加入set
            auto it = ys.lower_bound(y); //>=当前任务y值的最小y
            if(it != ys.end())
            {
                cnt ++;
                res += 500 * x + 2 * y;
                ys.erase(it); // 删掉机器
            }
        }
        cout << cnt << ' ' << res <<endl;
    }
    return 0;
}
posted @ 2019-05-17 09:17  WMXNLFD  阅读(302)  评论(0编辑  收藏  举报