TJU[2023暑期训练]个人排位赛(九)

A-Cancel the Trains

题目描述

Gildong 有一列车系统。从左到右,从下到上分别有100头列车。从每侧出发的列车从 1 到 100 编号,所有列车的速度相同。如图。

列车系统可以用二维平面上的坐标表示。每一头列车视为一个点,从底部开始的第 i 头列车最初在 (i,0) ,并将在 T 分钟后到达 (i,T),从左端开始的第 i 列车最初在 (0,i) ,在 T 分钟后到达 (T,i) 。所有列车在 101分钟后到达目的地。

然而,Gildong 发现,一些在特定时间发车的列车,是存在危险的。这时,有 n 头列车计划从底端发车,m 列车计划从左端发车。如果两列火车同时处于 (x,y) 的位置,那么它们会相撞。因此,Gildong 要求你找出应该取消的最小列车数,以防止所有碰撞发生。

输入格式

每个数据点包含一个或多组数据。第一行包含测试组数t(1≤t≤100)。

每个测试数据包含三行。

第一行两个整数 n 和m(1≤n,m≤100),代表计划从底端出发的列车数和计划从左端出发的列车数。

第二行包含 n 个整数。每个整数代表从底端开始的列车号。这些数字是按严格递增的顺序给出的,介于 1 到 100 之间(含 1 和 100)。

第三行包含 m 个整数。每个整数代表从左端开始的列车号。这些数字是按严格递增的顺序给出的,介于 1 到 100之间(含 1 和 100)。

输出格式

对于每个测试用例,输出一个整数:为了防止所有碰撞,应该取消的最小列车数。

Sample Input

3
1 2
1
3 4
3 2
1 3 4
2 4
9 14
2 7 16 28 33 57 59 86 99
3 9 14 19 25 26 28 35 41 59 85 87 99 100

Sample Output

0
1
3

题目分析

签到题。。。转化为:计算从底端出发和从左端出发的列车具有相同标号大小的个数。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		map<int, int>a, b;
		int num = 0;
		int te;
		int n, m;
		cin >> n >> m;
		for (int i = 0; i < n; i++) {
			cin >> te;
			a[te] = 1;
		}
		for (int i = 0; i < m; i++) {
			cin >> te;
			b[te] = 1;
		}
		for (int i = 1; i <= 100; i++) {
			if (a[i] && b[i]) {
				num++;
			}
		}
		cout << num << endl;
	}
	return 0;
}

B-Suffix Operations

题目描述

给你一个整数序列,其中有n个元素。你需要对这个序列进行操作。

1 在所有操作开始前,你可以选择一个数,并修改他的值,这个值你可以自己定。本操作无花费。

2 选择一个下标i,将所有下标不小于i的元素加上一个整数x,x可以你自己定。这次操作花费为x的绝对值。

本题给你一个序列,要你求将这个序列中的元素统一,至少花费多少。

输入格式

第一行一个整数,测试数据数量。

对于每个测试数据,第一行一个整数n,元素的个数。接下来一行n个整数,为元素的值。

输出格式

对于每组数据,输出最小花费,占一行。

注意,元素值可能为负数或0。

Sample Input

7
2
1 1
3
-1 0 2
4
99 96 97 95
4
-3 -5 -2 1
6
1 4 3 2 4 1
5
5 0 0 0 5
9
-367741579 319422997 -415264583 -125558838 -300860379 420848004 294512916 -383235489 425814447

Sample Output

0
1
3
4
6
5
2847372102

 题目分析 

 这道题不算难。

 先假设不修改任何一个数,那么最终的花费将是整个序列差分数组元素的和mx。接下来去修改某个值,该值与其边上两点的关系有如下两种可能 单调 不单调。

使得花费最小的情况是使该点在 左序列右端点 和 右序列左端点 中间,即这三个点满足一个单调关系。

(1 )单调时:满足上述情况。(2)不单调时:需要使该点满足上述情况。

所以,从第二个点遍历到第N-1个点,使它们满足上述情况并修改最后的mx,

mx = min(mx, ans - abs(a[i] - a[i - 1]) - abs(a[i] - a[i + 1]) + abs(a[i - 1] - a[i + 1]));

对于整个序列左端点和右端点进行特判,

mx = min(mx, ans - abs(a[1] - a[2]));  //左端点特判

mx = min(mx, ans - abs(a[n - 1] - a[n]));  //右端点特判

最后,mx即为所求最终花费。

 AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;

ll a[200000 + 10]; //a[]数组存放序列元素
int b[200000 + 10];//b[]存差分数组
int main()
{
	int t;//表示有t组数据
	cin >> t;
	while (t--)
	{
		ll n;  //每组数据n个数
		cin >> n;
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i];
		}
		ll ans = 0; //统计差分数组的总和
		for (int i = 2; i <= n; i++)
		{
			b[i] = abs(a[i] - a[i - 1]);

			ans += b[i];
		}
		ll mx = 1e18;
		for (int i = 2; i < n; i++)
		{
			mx = min(mx, ans - abs(a[i] - a[i - 1]) - abs(a[i] - a[i + 1]) + abs(a[i - 1] - a[i + 1]));
		}
		mx = min(mx, ans - abs(a[1] - a[2]));  //左端点特判

		mx = min(mx, ans - abs(a[n - 1] - a[n]));  //右端点特判

		cout << mx << endl;

	}
	return 0;
}

G-Buy the String

题目描述

给出四个整数 n,c0​,c1​ 和 h ,以及一个长度为 n 的字符串 s ,且 s 仅由 0 和 1组成。你可以每次以 h 的花费修改字符串中的一个字符。在经过一些修改后,你需要买下这个字符串,其中字符 0需要花费 c0​ ,字符 1 需要花费 c1​ 。求最小花费。

Sample Input

6
3 1 1 1
100
5 10 100 1
01010
5 10 1 1
11111
5 1 10 1
11111
12 2 1 10
101110110101
2 100 1 10
00

Sample Output

3
52
5
10
16
22

题目分析

 签到题。。。如果c0>c1+h,就将0换成1,如果c1>c0+h,就将1换成0。

AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int sum = 0;
		int num0 = 0, num1 = 0;//num0为0的个数,num1为1的个数
		int n, c0, c1, h;
		cin >> n >> c0 >> c1 >> h;  //c0为购买一个0的花费,c1为购买一个1的花费
		string s;
		cin >> s;
		for (int i = 0; i < s.length(); i++) {
			if (s[i] == '0') num0++;	
			else num1++;
		}
		//cout << num0 << " " << num1 << endl;
		if (c0 > h + c1)  sum += (h + c1) * num0;
		else  sum += c0 * num0;
		
		if (c1 > h + c0)  sum += (h + c0) * num1;
		else  sum += c1 * num1;

		cout << sum << endl;
	}

	return 0;
}

H -Sum of Medians

题目描述

 题目翻译

给定n×k 个正整数,要求将这些数分成 k 组,每组 n 个,并且希望每一组第 ⌈2n​⌉ 大的数的和尽可能大,请求出这个和。

 

 Sample Input 

6
2 4
0 24 34 58 62 64 69 78
2 2
27 61 81 91
4 3
2 4 16 18 21 27 36 53 82 91 92 95
3 4
3 11 12 22 33 35 38 67 69 71 94 99
2 1
11 41
3 3
1 1 1 1 1 1 1 1 1

 Sample Output

165
108
145
234
11
3

题目分析

这道题也不算难。

先求出题意描述的中位数的位置Z(可以用上次学到的向上取整的表达式)。用前面(Z-1)*k个数one by one填充k组中位数前的位置,从(Z-1)*k+1个数开始,每隔n-Z+1取数,并将这些数相加,即为满足题意的最大中位数之和。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
	ll t;
	cin >> t;
	while (t--)
	{
		ll temp;
		vector<int>w;
		ll n, k;
		cin >> n >> k;
		w.push_back(-1);//先存入一个值,使得后面n*k个数中第一个数的下标为1
		for (ll i = 0; i < n * k; i++) {
			cin >> temp;  w.push_back(temp);
		}
		ll zhong = (n - 1) / 2 + 1;  //找到题意中位数的位置,这里用向上取整的方法。
		ll sum = 0;
		for (ll j = (zhong - 1) * k + 1; j <= n * k; j += n - zhong + 1) {
			sum += w[j];
		}
		cout << sum << endl;
	}
	return 0;
}

I -Binary Table (Easy Version)

题目描述

 题意

给定一个 n×m 的 01 矩阵,每次操作可以将某个 2×2 的矩阵内的 3 个数取反,请在 3×n×m 步内将矩阵变为全 0。

 题目分析

我们可以每次只改变一个点。如何做到呢?

? 1
? ?

 我们在这个矩阵中找到所有 ? 点作为中心 ,依次对其它三个点进行取反: 第一次,找到左上角的 ? ,对另外三个点进行取反。

? 0
¿ ¿

 反问号¿表示原来的?取反后的值。
第二次:找到左下角的 ¿ ,对另外三个点进行取反。

¿ 1
¿ ?

第三次:找到右下角的 ? ,对另外三个点进行取反。 

? 0
? ?

这样,原来的 1 就变成了 0,并且其它点不受影响,也就是说,我们每找到一个 1 , 就可以把它放到一个 2∗2 的矩阵里,以另外三个点作为中心(即不取反的那个,无论是 0 还是 1)对与其互斥的三个点取反,就能达到目标,即使矩阵全部都是 1 , 也能在 3nm 步内完成。 

但是每次把“1”的位置作为2*2矩阵的哪个位置是需要讨论的,如下所示。

⭐的位置是“1”在2*2矩阵的位置。分为四种情况:

AC代码

#include<bits/stdc++.h>
using namespace std;
int main() {
	int t;  //t组数据
	cin >> t;
	while (t--)
	{
		vector<string>srr;
		int n, m; //n行m列
		cin >> n >> m;
		string s;
		for (int i = 0; i < n; i++) {
			cin >> s;
			srr.push_back(s);
		}
		int ans = 0;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (srr[i][j] == '1') {
					ans += 3;
				}
			}
		}
		cout << ans << endl;

		for (int i = 0; i < n - 1; i++) {  
			for (int j = 0; j < m - 1; j++) {
				if (srr[i][j] == '1') {
					cout << i + 1 << " " << j + 1 << " " << i + 2 << " " << j + 1 << " " << i + 2 << " " << j + 2 << endl;
					cout << i + 1 << " " << j + 1 << " " << i + 2 << " " << j + 2 << " " << i + 1<< " " << j + 2 << endl;
					cout << i + 1 << " " << j + 1<< " " << i + 1 << " " << j + 2 << " " << i + 2 << " " << j + 1 << endl;
				}
			}
		}
		for (int j = 0;j < m - 1; j++) {
			if (srr[n - 1][j] == '1') {
				cout << n - 1 + 1 << " " << j + 1 << " " << n - 1  << " " << j + 1 << " " << n - 1 << " " << j+2 << endl;
				cout << n - 1 + 1 << " " << j + 1 << " " << n - 1+1  << " " << j +2 << " " << n - 1<< " " << j+2 << endl;
				cout << n - 1 + 1 << " " << j + 1 << " " << n - 1  << " " << j + 1 << " " << n - 1+1 << " " << j+2<< endl;
			}
		}
		for (int i = 0; i < n - 1; i++) {
			if (srr[i][m - 1] == '1') {
				cout << i + 1 << " " << m - 1 + 1 << " " << i + 2 << " " << m - 1 + 1 << " " << i + 2 << " " << m - 1 << endl;
				cout << i + 1 << " " << m - 1 + 1 << " " << i + 1 << " " << m - 1  << " " << i + 2 << " " << m - 1 << endl;
				cout << i + 1 << " " << m - 1 + 1 << " " << i + 1 << " " << m - 1  << " " << i + 2 << " " << m - 1+1 << endl;
			}
		}
		if (srr[n - 1][m - 1] == '1') {
			cout << n - 1 + 1 << " " << m - 1 + 1 << " " << n - 1 << " " << m - 1 + 1 << " "<<n - 1 << " " << m - 1 << endl;
			cout << n - 1 + 1 << " " << m - 1 + 1 << " " << n - 1+1 << " " << m - 1  << " " << n - 1  << " " << m - 1 << endl;
			cout << n - 1 + 1 << " " << m - 1 +1 << " " << n - 1+1 << " " << m - 1  << " " << n - 1  << " " << m - 1 +1<< endl;
		}
	}

	return 0;
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值