活动安排
链接:https://ac.nowcoder.com/acm/contest/950/A
来源:牛客网
题目描述
设有个n活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i的时间为[start_time, end_time);
选择出由互相兼容的活动组成的最大集合。
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 3e4 + 10;
struct node { int x, y; }p[N];
bool cmp(node a, node b) { return a.y < b.y; }
int main() {
ios::sync_with_stdio(false);
cin.tie(0);// cout.tie(0);
int k; cin >> k;
for (int i = 0; i < k; i++) {
cin >> p[i].x >> p[i].y;
}
sort(p, p + k, cmp); //定右值,早结束,早开始
int res = 1; //初始值定为1,是有第一个一定可以安排
node now = p[0]; //从第一个开始为当前最优选择
for (int i = 1; i < k; i++) {
if (now.y <= p[i].x) res++, now = p[i];
//找到下一个满足条件的情况,因为排序,所以第一个满足的就是最优情况,更改此项为当前最优情况,计数加一
}
cout << res << "\n";
return 0;
}
种树
链接:https://ac.nowcoder.com/acm/contest/950/B
来源:牛客网
题目描述
某条街被划为 n 条路段,这 n 条路段依次编号为1\dots n1…n。每个路段最多可以种一棵树。现在居民们给出了 h 组建议,每组建议包含三个整数 b,e,t,表示居民希望在路段 b 到 e 之间至少要种t棵树。这些建议所给路段的区间可以交叉。请问:如果要满足所有居民的建议,至少要种多少棵树。
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 3e4 + 10;
struct node { int x, y, c; }p[N];
bool cmp(node a, node b) { return a.x < b.x; } //所贪的位置,左值排序,逆序贪左
// 右值排序,正序贪右
int main() {
ios::sync_with_stdio(false);
cin.tie(0);// cout.tie(0);
int n, k; cin >> n >> k;
for (int i = 0; i < k; i++) {
cin >> p[i].x >> p[i].y >> p[i].c;
}
sort(p, p + k, cmp);
int res = 0;
//逆序贪左
//思想,以左边的值排序后,从后向前,将树种在左边,左边的区域是可以被前面的区间重复计算的(在区间有交叉的情况下)
for (int i = k - 1; i >= 0; i--) {
int cnt = 0;
for (int j = p[i].x; j <= p[i].y; j++) {
if (book[j]) cnt++;
}
for (int j = p[i].x; j <= p[i].y; j++) {
if (cnt >= p[i].c) break;
if (!book[j]) book[j] = 1, res++, cnt++;
}
}
//正序贪右
//思想,以右边的值排序后,从前向后,将树种在右边,右边的区域是可以被后面的区间重复计算的(在区间有交叉的情况下)
//bool cmp(node a, node b) { return a.y < b.y; }
//sort(p, p + k, cmp);
//int res = 0;
//for (int i = 0; i < k; i++) {
// int cnt = 0;
// for (int j = p[i].x; j <= p[i].y; j++) {
// if (book[j]) cnt++;
// }
// for (int j = p[i].y; j >= p[i].x; j--) {
// if (cnt >= p[i].c) break;
// if (!book[j]) book[j] = 1, res++, cnt++;
// }
//}
cout << res << "\n";
return 0;
}
喷水装置
链接:https://ac.nowcoder.com/acm/contest/950/C
来源:牛客网
题面
长L米,宽W米的草坪里装有n个浇灌喷头。每个喷头都装在草坪中心线上(离两边各 W/2 米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。
请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头?
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2e4 + 10;
const double eps = 1e-10;
struct node { double x, y; }p[N];
double l, w;
bool cmp(node a, node b) {
return a.x < b.x;
}
double deal(double r) { // 求圆心所在的横坐标点到圆与轴相交的点的距离
double tmp = sqrt(r * r - w * w / 4);
return tmp > eps ? tmp : 0.0; //当半径小于宽的一半时为负值,此时将距离返还为零,不影响其他区间
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while (t--) {
int n, res = 0; cin >> n >> l >> w;
double d, r, dist, now = 0,nex = 0;
for (int i = 0; i < n; i++) {
cin >> d >> r;
dist = deal(r);
p[i].x = d - dist, p[i].y = d + dist; //转换为区间
}
sort(p, p + n, cmp); //排序,定左值
int i = 0;
while (now < l) { //贪右值
if (now < p[i].x) break; //断点情况判断 方法1
while (i < n && p[i].x <= now) nex = max(nex, p[i].y), i++; //遍历满足左值的情况,取最大的右值
while (i < n && p[i].y <= nex) i++; //跳过当前最优区间包裹的小区间
//if (now == nex) break; //断点情况判断 方法2
now = nex, res++;
}
if (now >= l) cout << res << "\n";
else cout << "-1" << "\n";
}
return 0;
}
加工生产调度
链接:https://ac.nowcoder.com/acm/contest/950/D
来源:牛客网
题目描述
某工厂收到了n个产品的订单,这n个产品分别在 A、B 两个车间加工,并且必须先在 A 车间加工后才可以到 B 车间加工。
某个产品i在 A,B 两车间加工的时间分别为 Ai, Bi 。怎样安排这n个产品的加工顺序,才能使总的加工时间最短。
这里所说的加工时间是指:从开始加工第一个产品到最后所有的产品都已在 A,B 两车间加工完毕的时间。
第一行一个数据,表示最少的加工时间;
第二行是一种最小加工时间的加工顺序。
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 3e4 + 10;
struct node { int x, y, id; }p[N];
//这题确实很难,因为明明是贪心,可排序就解决了所有问题
bool cmp(node a, node b) {
if (a.x < a.y && b.x < b.y) return a.x < b.x;
// 两个产品都是在A加工的时间小于B加工的时间,优先加工A小的
//比如 [3 ,5] [4, 6] ,优先加工A小的总时间为14, 反之为15
else if (a.x >= a.y&& b.x >= b.y) return a.y > b.y;
//两个产品都是B加工时间大于等于A加工时间,优先选择B大的
//比如[5, 3] [6, 4] , 优先选择B大的总时间为14, 反之为15
return a.x < a.y;
//其余情况优先选择A加工时间小于B加工时间的
//比如 [3, 5] [6, 4] ,优先选择A加工时间小于B加工时间的总时间为 13, 反之为 15
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);// cout.tie(0);
int n; cin >> n;
for (int i = 0; i < n; i++) cin >> p[i].x;
for (int i = 0; i < n; i++) cin >> p[i].y;
for (int i = 0; i < n; i++) p[i].id = i + 1;
sort(p, p + n, cmp); // sort 和 stable_sort 在这题都能Ac, 并不存在稳不稳定的问题,别的OJ我就不清楚了
int t1 = 0, t2 = 0; //模拟加工时间
for (int i = 0; i < n; i++) {
t1 += p[i].x; //t1表示A的加工时间计数
if (t1 > t2) t2 = t1; //B需要在A加工完毕后才可以开始,时间要同步
t2 += p[i].y; //加上B加工的时间
}
cout << t2 << "\n";
cout << p[0].id;
for (int i = 1; i < n; i++) cout << " " << p[i].id;
cout << "\n";
return 0;
}
智力大冲浪
链接:https://ac.nowcoder.com/acm/contest/950/E
来源:牛客网
题面
小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者m元。先不要太高兴!因为这些钱还不一定都是你的?!接下来主持人宣布了比赛规则:
首先,比赛时间分为n个时段,它又给出了很多小游戏,每个小游戏都必须在规定期限ti前完成。如果一个游戏没能在规定期限前完成,则要从奖励费m元中扣去一部分钱ai, ai为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。主持人只是想考考每个参赛者如何安排组织自己做游戏的顺序。作为参赛者,小伟很想赢得冠军,当然更想赢取最多的钱!
注意:比赛绝对不会让参赛者赔钱! 这句话毫无意义
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
struct node { int t, num; }p[N];
int sum[N]; //sum[i] 表示在 i 时间内 安排的游戏数
bool cmp(node a, node b) { return a.num > b.num; }
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int m, n, t = 0, res = 0; cin >> m >> n;
for (int i = 0; i < n; i++) cin >> p[i].t;
for (int i = 0; i < n; i++) cin >> p[i].num;
sort(p, p + n, cmp); //按扣除钱数大的排序
int now = 0; //标记时间点,在此时间点以前的时间已经安排满游戏了
for (int i = 0; i < n; i++) {
if (p[i].t > now) { //此游戏时间点大于标记时间点,可以被安排
for (int j = p[i].t; j <= n; j++) { //此游戏时间点以后的时间点都要加一记录,计算在以后的时间内
if (++sum[j] == j) now = j; //当有满足时间内安排满的情况,更新标记时间点,遍历会取最大值
}
}
else res += p[i].num; //不能安排的游戏,记录扣除的钱数
}
cout << m - res << "\n";
return 0;
}
数列极差
链接:https://ac.nowcoder.com/acm/contest/950/F
来源:牛客网
题面
佳佳的老师在黑板上写了一个由n个正整数组成的数列,要求佳佳进行如下操作:每次擦去其中的两个数a和b,然后在数列中加入一个数a * b + 1 ,如此下去直至黑板上剩下一个数为止,在所有按这种操作方式最后得到的数中,最大的为max,最小的为min, 则该数列的极差定义为 M= max−min 。
由于佳佳忙于准备期末考试,现请你帮助他,对于给定的数列,计算出相应的极差M。
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
priority_queue<int, vector<int>, less<int> > Q1; //从大到小
priority_queue<int, vector<int>, greater<int> > Q2; //从小到大
int main() {
int n, x, y; cin >> n;
for (int i = 0; i < n; i++) {
cin >> x;
Q1.push(x), Q2.push(x);
}
cin >> x;
// 当有三个数时,例如 a, b, c
//(a*b + 1) * c + 1 = a*b*c + c + 1
//(a*c + 1) * b + 1 = a*b*c + b + 1
//(b*c + 1) * a + 1 = a*b*c + a + 1
//若a <= b <= c
//可以看出, 大的数后乘,最终得到的结果最大
// 大的数先乘,最终得到的结果最小
if (n) {
while (Q1.size() > 1) {
x = Q1.top(); Q1.pop();
y = Q1.top(); Q1.pop();
Q1.push(x * y + 1);
}
while (Q2.size() > 1) {
x = Q2.top(); Q2.pop();
y = Q2.top(); Q2.pop();
Q2.push(x * y + 1);
}
cout << abs(Q1.top() - Q2.top()) << "\n";
}
else cout << "0\n";
return 0;
}
数列分段
链接:https://ac.nowcoder.com/acm/contest/950/G
来源:牛客网
题目描述
对于给定的一个长度为N的正整数数列a1,a2,a3…an,现要将其分成连续的若干段,并且每段和不超过M(可以等于M),问最少能将其分成多少段使得满足要求。
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 5e4 + 10;
int main() {
int n, m, x, res = 0, sum = 0;
cin >> n >> m;
//感觉这题算不上贪心,但若按分类还是算贪心
for (int i = 0; i < n; i++) {
cin >> x; //sum 用来记录区间的总和
if (sum + x == m) res++, sum = 0; //总和正好等于临界值,区间数加一,总和变为零,开启新区间的累加和
else if (sum + x > m) res++, sum = x; //区间和加上大于临界值,不能加,以此值作为新区间的第一个数值
else sum += x; //像不像在手把手教学
}
if(sum!=0) res++;
cout << res << "\n";
return 0;
}
线段
链接:https://ac.nowcoder.com/acm/contest/950/H
来源:牛客网
题目描述
数轴上有n条线段,选取其中k条线段使得这k条线段两两没有重合部分,问k最大为多少。
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
struct node { int x, y; }p[N];
bool cmp(node a, node b) { return a.y < b.y; }
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, res = 1; cin >> n;
for (int i = 0; i < n; i++) cin >> p[i].x >> p[i].y;
sort(p, p + n, cmp); //此题类似于活动安排, 早结束,早开始, 不重合,不冲突
node now = p[0]; //当前最优
for (int i = 1; i < n; i++) {
if (p[i].x >= now.y) now = p[i], res++; //满足情况就取
}
if (!n) res = 0;
cout << res << "\n";
return 0;
}
家庭作业
链接:https://ac.nowcoder.com/acm/contest/950/I
来源:牛客网
题目描述
老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为 10,要求在 6 天内交,那么要想拿到这 10 学分,就必须在第 6 天结束前交。
每个作业的完成时间都是只有一天。
你的任务就是找到一个完成作业的顺序获得最大学分。
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e6 + 10;
struct node { int x, y; }p[N]; //x代表期限, y代表分值
priority_queue<int, vector<int>, less<int> > Q; //优先队列, 从大到小
bool cmp(node a, node b) { return a.x > b.x; }; //从大到小
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n; cin >> n;
for (int i = 0; i < n; i++) cin >> p[i].x >> p[i].y;
sort(p, p + n, cmp); //x代表期限, 按期限从大到小排列
int res = 0;
// 将时间点设置在最后一天,然后倒着取,因为期限大的,可以在之前有空的那天完成
int now = p[0].x; //取最大的期限那天
for (int i = 0; i < n; i++) {
if(now > p[i].x ){
//这点是由于,须先将当天的放入完毕,取分值最大的
//例如 : [6 2] [6 4] [6 7] [2 1]
//前三个截止日期都是6,所以要将这天的都入队
//然后取最大的在当天做
while (Q.size() && now > p[i].x) {
res += Q.top(); Q.pop(); now--;
}
//还看上面例子,队列里有3个数,分别对应第六天截止的三份作业
//然而,第三,四,五天都没有作业,所以可以提前做好
//为什么要倒着取,是因为第一天截至的两次作业,你只能完成其中一个,第二即使放起来明天做也没用,已经截止了
//但是若是倒着取,就像例子一样,第六天的作业有3份,当天完成一份,其余的可以提前完成
//但是倒着取的话要利用队列取最大值,因为若是第六天有9份作业截至而只有3,4,5是空闲的,无法取完,只取最大的3份
if(!Q.size()) now = p[i].x;
//此处为跳转,中间出现断点情况,中间好多天空闲,但没有以后的作业可以做
}
Q.push(p[i].y); //无论如何都要装进去,它进去后,当满足可取条件便可取出
}
while (Q.size() && now) { //若时间还未到零(写作业肯定从第一天开始写了),队列中还有可以拿分的作业
res += Q.top(); Q.pop();
now--;
}
cout << res << "\n";
return 0;
}
codeforces 1140C Playist
题面
学长A 是一个多才多艺的男人, 凭借着动人的歌喉吸引了大量的学弟学妹。
学长A 会很多首歌曲,每首歌曲都有它的长度ti和好听度bi.
听一组学长A 唱的歌曲得到了愉悦感是该组歌曲的总长度乘于其中歌曲的最小的好听度。
例如,听一组一共3首歌曲, 有长度(5, 7, 4)和好听度 (6, 11, 14)
总愉悦感 = (5 + 7 + 4) * 6 = 96。
学长A 说出他会的歌曲数n, 并列出他们的长度和好听度,让你至多选择k首。
你要选择出听哪些歌得到的愉悦感是最大的。
(看自己写的题面,我也是陶醉了)
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
struct node { int x, y; }p[N];
priority_queue<int, vector<int>, greater<int> > Q;
bool cmp(node a, node b) { return a.y > b.y;}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, k; cin >> n >> k;
for (int i = 0; i < n; i++) cin >> p[i].x >> p[i].y;
sort(p, p + n, cmp); //按好听度从大到小排序
ll sum = 0, res = 0; //sum 为所取的歌曲长度总和, res记录愉悦感所能取到的最大值
for (int i = 0; i < n; i++) {
sum += p[i].x;
Q.push(p[i].x);
if (Q.size() > k) { //若是队列中的数目大于可取的最大数目,减去其中最小的
//最初我一直在疑惑,若是减去的是此时的p[i].x,那么p[i].y不就无法使用了
//其实,因为是按好听度从大到小排序,所以此次减去的是此次的p[i].x,那么sum将和上次的值一样,没有变化
//但是上次sum * p[i - 1].y必定大于等于此次的结果,(按y排序,p[i-1].y >= p[i].y) 此次结果对最终无影响
sum -= Q.top(); Q.pop();
}
res = max(res, sum * p[i].y);
}
cout << res << "\n";
return 0;
}