第二节 基础算法 - 2

CF1359A 题解

洛谷链接&CF 链接

题目简述

共有 \(T\) 组数据。

对于每组数据给出 \(n,m,k\),表示 \(k\) 名玩家打牌,共 \(n\) 张牌,\(m\) 张王,保证 \(k \mid n\),记得分为拿到最多王的玩家手中王数减去拿到第二多王的玩家手中的王数,求得分最大值

思路

经典贪心题。

首先需特判两种情况

  1. \(m\)\(0\)

  2. \(n / k \ge m\)

首先对于情况 \(1\),直接输出 \(0\) 即可,因为没有王

对于情况 \(2\),直接输出 \(m\) 即可,构造就是\(m\) 张王全给一个人,这样最大差就是 \(m\)

对于一般情况,就先把能给的王全给一个人其余王平均分即可

具体代码实现如下:

#include<iostream>
using namespace std;

int T, n, m, k;

int main(){
	cin >> T;
	while(T --) {
		cin >> n >> m >> k;
		int num = n / k; // 计算每个人牌数
		if(m == 0) { cout << "0\n"; continue; } // 特判情况 1
		if(num >= m) { cout << m << endl; continue; } // 特判情况 2
		cout << num - (m - num) / (k - 1) - ((m - num) % (k - 1) != 0) << endl; // 公式,上有构造及证明
	}
	return 0;
}

提交记录

\[\text{The End!} \]

CF1363A 题解

洛谷链接&CF 链接

题目简述

共有 \(T\) 组数据。

对于每组数据,给定 \(n,x\)\(n\) 个数,问是否可以从 \(n\) 个数中选 \(x\) 个使其和为奇数,可以输出 Yes,否则输出 No

思路

首先在输入过程中记录奇数的个数,接着特判没有奇数的情况,直接输出 No 即可

接着如果奇数有偶数个,则把奇数个数减一,因为我们拿着偶数个奇数没有任何用处,所以扔掉一个即可。

下面判断 \(x\) 是否大于奇数个数,如果大于且剩余偶数可补上空缺,可输出 Yes;如果不大于则判断是否有偶数或 \(x\) 为奇数,满足其中一个即可输出 Yes;否则输出 No

下面是代码实现:

#include<iostream>
using namespace std;

int T, n, x;

int main() {
	cin >> T; // 多组输入
	while(T --) {
		int sum = 0; // 初始化奇数个数
		cin >> n >> x;
		for(int i = 1, tmp; i <= n; i++) {
			cin >> tmp;
			if (tmp & 1) sum++; // 如果是奇数则加 1
		}
		if(!sum) {
			cout << "No\n"; // 没有奇数特判输出 No
			continue; // 直接返回
		} 
		if(!(sum & 1)) sum --, n --; // 奇数个数为偶数处理
		if(x >= sum && (x - sum) <= (n - sum)) { cout << "Yes\n"; continue; } // 情况 1
		if(x < sum) 
			if((n - sum) > 0 || x % 2 != 0) { cout << "Yes\n"; continue; } // 情况 2
		printf ("No\n"); // 情况 3,直接输出 No
	}
	return 0;
}

提交记录

\[\text{The End!} \]

CF1282A 题解

洛谷链接&CF 链接

题目简述

共有 \(T\) 组数据。

有一条数轴,要从 \(a\) 点跑到 \(b\) 点,在 \(c\) 点的地方有网络,覆盖区间为 \([c - r, c+ r]\),问在多少时间没有网络覆盖。

思路

分类讨论:

  1. \(c + r \le a\)\(c - r \ge b\),表示区间 \([a, b]\) 内都没有网络,直接输出 \(b - a\)

  2. \(c + r \ge b\)\(c - r \ge a\),表示区间 \([c - r, b]\) 区间有网络,输出 \(c - r - a\)

  3. \(c - r \le a\)\(c + r \le b\),表示区间 \([a, c + r]\) 有网络,输出 \(b - c - r\)

  4. \(c + r \ge b\)\(c - r \le a\),表示区间 \([a, b]\) 内都有网络,输出 \(0\) 即可。

  5. \(c + r \le b\)\(c - r \ge a\),表示区间 \([c - 1, c + r]\) 有网络,正常输出 \(b - a - 2 \times r\) 即可。

那么代码就很好实现了:

#include<iostream>
using namespace std;

int T, a, b, c, r;

int main(){
	cin >> T;
	while(T --) {
		cin >> a >> b >> c >> r;
		if(a > b) swap(a, b); // 保证 a <= b
		if(c + r <= a || c - r >= b) cout << b - a << endl; // 情况 1
		else if(c + r >= b && c - r >= a) cout << c - r - a << endl; // 情况 2
		else if(c - r <= a && c + r <= b) cout << b - c - r << endl; // 情况 3
		else if(c + r >= b && c - r <= a) cout << 0 << endl; // 情况 4
		else cout << b - a - 2 * r << endl;  // 情况 5
	}
	return 0;
}

提交记录

\[\text{The End!} \]

CF479C 题解

洛谷链接&CF 链接

题目简述

一个人想要安排期末考试的时间。

\(n\) 场考试,每场考试有两个时间 \(x_i,y_i\),一个是老师规定的时间,另外一个是他与老师商量好的考试时间。

如果错过了,那就只能按照原来的考试时间考试。

规定:只能按照原定考试的日期顺序进行考试的情况下,输出考完试的日期。

思路

简单思维题,因为只能按照原定顺序进行考试,所以用结构体储存两个时间点,接着按照 \(x_i\) 的非递减顺序排序,依次遍历,如果当前 \(ans\) 小于 \(y_i\) 更新 \(ans\) 的值即可,否则 \(ans\) 赋值为当前 \(x_i\) 的值。

下面就是代码实现:

#include<iostream>
#include<algorithm> 
using namespace std;

struct node {
	int x, y; // 记录两个时间点
} p[5005]; // 注意数据范围
int n; 

// 排序函数,上面解释过
bool cmp(node a, node b) {
	if(a.x == b.x) return a.y < b.y;
	return a.x < b.x; 
}

int main(){
	cin >> n;
	for(int i = 1; i <= n; i ++) cin >> p[i].x >> p[i].y; // 输入
	sort(p + 1, p + n + 1, cmp); // 按照 x 的非递减顺序排序
	int ans = p[1].y; // 初始化 ans
	for(int i = 1; i <= n; i ++)
		if(ans <= p[i].y) ans = p[i].y; // 上述情况 1
		else ans = p[i].x; // 上述情况 2
	cout << ans << endl; // 输出
	return 0;
}

提交记录

\[\text{The End!} \]

CF1468N 题解

洛谷链接&CF 链接

题目简述

共有 \(T\) 组数据,对于每组数据:

有三个桶,五种垃圾,每个桶有固定的容量。

前三种垃圾分别放入三种桶中,第四种垃圾可以放进 \(1,3\) 桶中,第五种垃圾可以放进 \(2,3\) 桶中。

问题:对于给定的桶容量和垃圾量,请问垃圾是否可以全部放入桶中?

思路

简单贪心题。

贪心思路很简单,首先装 \(1,2,3\) 种垃圾,如果此时都装不下直接输出 NO,因为这三种垃圾只能装入 \(1,2,3\) 号桶。

那么接下来就是处理剩下两种垃圾,发现这两种垃圾都可装入 \(3\) 号桶,所以需要优先装 \(1,2\) 号桶,装不下之后再装 \(3\) 号,如果此时装不下,则输出 NO,否则输出 YES

下面是代码实现:

#include<iostream>
using namespace std;

int T, a, b, c, d, e, x, y, z; // 垃圾及桶

int main(){
	cin >> T; // 多组数据!
	while(T --) {
		cin >> x >> y >> z;
		cin >> a >> b >> c >> d >> e; // 分别输入垃圾数及桶数
		if(x < a || y < b || z < c) {
			cout << "NO\n"; // 1 2 3 装不下,输出 NO
			continue;
		}
		x -= a, y -= b, z -= c; // 能装下就装
		d -= x, e -= y; // 剩下两种垃圾,分别优先装入 1 2 号桶
		d = max(0, d), e = max(0, e); // 去除负数情况!!
		if(z >= d + e) cout << "YES\n"; // 剩下垃圾能装下
		else cout << "NO\n"; // 装不下
	}
	return 0;
}

提交记录

\[\text{The End!} \]

例题

逆序对

题目描述

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。

最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\)\(i<j\) 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

Update:数据已加强。

输入格式

第一行,一个数 \(n\),表示序列中有 \(n\)个数。

第二行 \(n\) 个数,表示给定的序列。序列中每个数字不超过 \(10^9\)

输出格式

输出序列中逆序对的数目。

样例输入 #1

6
5 4 2 6 3 1

样例输出 #1

11

提示

对于 \(25\%\) 的数据,\(n \leq 2500\)

对于 \(50\%\) 的数据,\(n \leq 4 \times 10^4\)

对于所有数据,\(n \leq 5 \times 10^5\)

请使用较快的输入输出

应该不会 \(O(n^2)\) 过 50 万吧 by chen_zhe

点击查看代码
#include<iostream>
#include<cstdio>
using namespace std;
#define MAXN 524288
int a[MAXN],tmp[MAXN],n;
long long merge(int a[],int l,int m,int r){
	long long step = 0;
	int i = l,j = m+1;
	for(int k = l;k <= r;k ++){
		if((j > r) || (i <= m) && (a[i] <= a[j])) tmp[k] = a[i ++];
		else tmp[k] = a[j ++], step += m-i+1;
	}
	for(int i = l;i <= r;i ++) a[i] = tmp[i];
	return step;
}

long long msort(int a[],int l,int r){
	if(l >= r) return 0;
	int mid = l+(r-l)/2;
	return msort(a,l,mid)+msort(a,mid+1,r)+merge(a,l,mid,r);
}

int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
	printf("%lld",msort(a,1,n));
	return 0;
}

[NOIP2015 提高组] 跳石头

题目背景

一年一度的“跳石头”比赛又要开始了!

题目描述

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 \(N\) 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 \(M\) 块岩石(不能移走起点和终点的岩石)。

输入格式

第一行包含三个整数 \(L,N,M\),分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 \(L \geq 1\)\(N \geq M \geq 0\)

接下来 \(N\) 行,每行一个整数,第 \(i\) 行的整数 \(D_i( 0 < D_i < L)\), 表示第 \(i\) 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式

一个整数,即最短跳跃距离的最大值。

样例输入 #1

25 5 2 
2
11
14
17 
21

样例输出 #1

4

提示

输入输出样例 1 说明

将与起点距离为 \(2\)\(14\) 的两个岩石移走后,最短的跳跃距离为 \(4\)(从与起点距离 \(17\) 的岩石跳到距离 \(21\) 的岩石,或者从距离 \(21\) 的岩石跳到终点)。

数据规模与约定

对于 \(20\%\)的数据,\(0 \le M \le N \le 10\)
对于 \(50\%\) 的数据,\(0 \le M \le N \le 100\)
对于 \(100\%\)的数据,\(0 \le M \le N \le 50000,1 \le L \le 10^9\)

点击查看代码
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
inline int read(){
  int x=0,w=1 ; char ch=0;
  while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
  while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getchar();}
  return x*w;
}
int l,n,m,a[100005],c,mid,ans;
bool pick_stone(int x){
	int last_p = 0,num = 0;
	for(int i=1;i<=n;i++){
		if(last_p+x > a[i])num++;
		else last_p = a[i];
	}
	if(num>m) return false;
	return true;
}
int main(){
	l=read();n=read();m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	int p=1,r=l;
	while(p<=r){
		mid = p+(r-p)/2;
		if(pick_stone(mid))ans=mid,p=mid+1;
		else r=mid-1;
	}
	cout<<ans;
	return 0;
}

[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 \(n-1\) 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 \(1\) ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 \(3\) 种果子,数目依次为 \(1\)\(2\)\(9\) 。可以先将 \(1\)\(2\) 堆合并,新堆数目为 \(3\) ,耗费体力为 \(3\) 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 \(12\) ,耗费体力为 \(12\) 。所以多多总共耗费体力 \(=3+12=15\) 。可以证明 \(15\) 为最小的体力耗费值。

输入格式

共两行。

第一行是一个整数 \(n(1\leq n\leq 10000)\) ,表示果子的种类数。

第二行包含 \(n\) 个整数,用空格分隔,第 \(i\) 个整数 \(a_i(1\leq a_i\leq 20000)\) 是第 \(i\) 种果子的数目。

输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 \(2^{31}\)

样例输入 #1

3 
1 2 9

样例输出 #1

15

提示

对于 \(30\%\) 的数据,保证有 \(n \le 1000\)

对于 \(50\%\) 的数据,保证有 \(n \le 5000\)

对于全部的数据,保证有 \(n \le 10000\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n, ans = 0;
priority_queue<int, vector<int>, greater<int> > q;
int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) {
		int x; 
		scanf("%d", &x);
		q.push(x);
	}
	while(q.size() > 1) {
		int x = q.top(); q.pop();
		int y = q.top(); q.pop();
		ans += x + y;
		q.push(x + y);
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2023-08-02 10:12  So_noSlack  阅读(16)  评论(0编辑  收藏  举报