目录
A. 「一本通 1.1 例 1」活动安排
题目描述
设有 n 个活动的集合E={1,2,..,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动 i 都有一个要求使用该资源的起始时间si 和一个结束时间fi,且 si<fi。如果选择了活动 i ,则它在时间区间[si,fi) 内占用资源。若区间[si,fi) 与区间 [sj,fj) 不相交,则称活动 i 与活动 j 是相容的。也就是说,当 fi≤sj 或fj≤si 时,活动 i 与活动 j 相容。选择出由互相兼容的活动组成的最大集合。
输入格式
第一行一个整数 n;
接下来的 n 行,每行两个整数 si 和 fi。
输出格式
输出互相兼容的最大活动个数。
样例
输入数据 1
4 1 3 4 6 2 5 1 7
Copy
输出数据 1
2
Copy
数据范围与提示
1≤n≤1000
区间问题
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,cnt,p;
struct app{
int l,r;
}a[1005];
bool cmp(app x,app y){//按右端点排序
return x.r<y.r;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].l>>a[i].r;
}
sort(a,a+n,cmp);
for(int i=1;i<n;i++){
if(a[i].l>=a[p].r){//如果左边界大于等于右边界,表示相容
cnt++;
p=i;
}
}
cout<<cnt+1;//加1是为了加上一开始的a[0]
return 0;
}
B. 「一本通 1.1 例 2」种树
题目描述
某条街被划为 �n 条路段,这 �n 条路段依次编号为 1…�1…n。每个路段最多可以种一棵树。现在居民们给出了 ℎh 组建议,每组建议包含三个整数 �,�,�b,e,t,表示居民希望在路段 �b 到 �e 之间至少要种 �t 棵树。这些建议所给路段的区间可以交叉。请问:如果要满足所有居民的建议,至少要种多少棵树。
输入格式
第一行为 �n,表示路段数。
第二行为 ℎh,表示建议数。
下面 ℎh 行描述一条建议:�,�,�b,e,t,用一个空格分隔。
输出格式
输出只有一个数,为满足所有居民的建议,所需要种树的最少数量。
样例
输入数据 1
9 4 1 4 2 4 6 2 8 9 2 3 5 2
Copy
输出数据 1
5
Copy
数据范围与提示
30%30% 的数据满足 0<�≤10000<n≤1000,0<ℎ≤5000<h≤500;
100%100% 的数据满足 0<�≤3×1040<n≤3×104,ℎ≤5000h≤5000,0<�≤�≤3×1040<b≤e≤3×104,�≤�−�+1t≤e−b+1。
种树,类似于区间选点
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int len,n;
ll cnt;
int v[30005];
struct app{
ll l,r,t;
}a[5005];
bool cmp(app x,app y){
return x.r<y.r;
}
int main(){
cin>>len;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].l>>a[i].r>>a[i].t;
}
sort(a,a+n,cmp);//按右端点排序先
for(int i=0;i<n;i++){
for(int j=a[i].l;j<=a[i].r;j++){//遍历整个区间
if(v[j])a[i].t--;//如果已经有树了,需要种的树减1
if(a[i].t==0)break;//等于0表示这个区间不需要再种树了
}
for(int k=a[i].r,j=a[i].t;j>0;k--,j--){//如果还需要种树,都放在区间末尾
if(!v[k])v[k]=1;//如果没被标记,就放树
else j++;//如果被标记了,那么种树的数量不能减小,加1来抵消减的
}
}
for(int i=1;i<=len;i++){//遍历整个区间看有多少棵树
if(v[i])cnt++;
}
cout<<cnt;
return 0;
}
C. 「一本通 1.1 例 3」喷水装置
题目描述
Source: UVa 10382
长 �L 米,宽 �W 米的草坪里装有 �n 个浇灌喷头。每个喷头都装在草坪中心线上(离两边各 �22W 米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。
请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头?
输入格式
输入包含若干组测试数据。
第一行一个整数 �T 表示数据组数;
每组数据的第一行是整数 �n、�L 和 �W;
接下来的 �n 行,每行包含两个整数,给出一个喷头的位置和浇灌半径(上面的示意图是样例输入第一组数据所描述的情况)。
输出格式
对每组测试数据输出一个数字,表示要浇灌整块草坪所需喷头数目的最小值。如果所有喷头都打开也不能浇灌整块草坪,则输出 −1−1 。
样例
输入数据 1
3 8 20 2 5 3 4 1 1 2 7 2 10 2 13 3 16 2 19 4 3 10 1 3 5 9 3 6 1 3 10 1 5 3 1 1 9 1
Copy
输出数据 1
6 2 -1
Copy
数据范围与提示
对于 100%100% 的数据,�≤15000n≤15000
数学+贪心,首先将中间的那个那条线上的通过计算来看最上面那条线,将问题简化成是否有区间可以覆盖整个草坪的长度,如果可以找出最小区间数
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct app{//m表示位置,w表示半径
double l,r;
}a[15005];
bool cmp(app x,app y){//按最左边排序
return x.l<y.l;
}
int main(){
int t;
cin>>t;
while(t--){
int n,l,w;
cin>>n>>l>>w;
int cnt=0;
for(int i=0;i<n;i++){
int x,y;
cin>>x>>y;
if(2*y>=w){//如果直径比宽度小直接out
a[cnt].l=x-sqrt(y*y-(w*w/4.0));
a[cnt++].r=x+sqrt(y*y-(w*w/4.0));
}
}
sort(a,a+cnt,cmp);
double p=0;//一开始的左端点
double ls=0;//表示临时变量
int ans=0;
for(int i=0;i<cnt;i++){
if(a[i].l<=p){//如果都比左端点小
ls=max(ls,a[i].r);//找最大的右端点
if(i==cnt-1){//如果到最后一个位置了,记录最大的右端点,答案数加1
p=max(p,ls);
ans++;
}
}
else{
if(ls==0){
ans=-1;
break;
}
ans++;
p=ls;//将最大的右端点记录
if(p>=l)break;//如果已经超过了直接打破循环
ls=0;//临时变量归0
i--;
}
}
if(p<l)ans=-1;//如果没有覆盖右端点
cout<<ans<<"\n";
}
return 0;
}
D. 「一本通 1.1 例 4」加工生产调度
题目描述
某工厂收到了 �n 个产品的订单,这 �n 个产品分别在 A、B 两个车间加工,并且必须先在 A 车间加工后才可以到 B 车间加工。
某个产品 �i 在 A,B 两车间加工的时间分别为 ��,��Ai,Bi。怎样安排这 �n 个产品的加工顺序,才能使总的加工时间最短。
这里所说的加工时间是指:从开始加工第一个产品到最后所有的产品都已在 A,B 两车间加工完毕的时间。
输入格式
第一行仅—个数据 �n ,表示产品的数量;
接下来 �n 个数据是表示这 �n 个产品在 A 车间加工各自所要的时间;
最后的 �n 个数据是表示这 �n 个产品在 B 车间加工各自所要的时间。
输出格式
第一行一个数据,表示最少的加工时间;
第二行是一种最小加工时间的加工顺序。
样例
输入数据 1
5 3 5 8 7 10 6 2 1 4 9
Copy
输出数据 1
34 1 5 4 2 3
Copy
数据范围与提示
对于 100%100%的数据, 0<�<1000,1≤��,��≤3500<n<1000,1≤Ai,Bi≤350。
本题的 SPJ 对行尾多余空格敏感,各位输出答案时不要留行尾多余空格~
关键问题就是不知道怎么排序,看一下代码里的注释,感兴趣的可以去了解一下Johnson法则
//递归关系:T(S, t) = min{ ai + T(S - {i}, bi) }
//Johnson法则:
//对于流水作业调度问题,必定存在一个最优调度π,
// 使得作业π(i)和π(i + 1)满足Johnson不等式
//min{bπ(i), aπ(i + 1)} >= min{bπ(i + 1), aπ(i )}。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n;
struct app{
int l,r,id;
}a[1005];
bool cmp(app x,app y){
return min(x.l,y.r)<min(x.r,y.l);
}
int dp[1005][1005];
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].l;
a[i].id=i+1;
}
for(int i=0;i<n;i++)cin>>a[i].r;
sort(a,a+n,cmp);
int cnt=a[0].l;//等于一开始工作的时间
for(int i=1;i<n;i++){
if(a[i-1].r>=a[i].l){//如果在b区间工作的时间比在a区间工作的时间长
cnt+=a[i-1].r;//答案加上最长的
a[i+1].l-=abs(a[i].l-a[i-1].r);//a区间不会等
}
else{
cnt+=a[i].l;
}
}
cnt+=a[n-1].r;//加上最后b区间工作的时间
cout<<cnt<<"\n";
for(int i=0;i<n;i++){
cout<<a[i].id;
if(i!=n-1)cout<<' ';
}
return 0;
}
E. 「一本通 1.1 例 5」智力大冲浪
题目描述
小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者 �m 元。先不要太高兴!因为这些钱还不一定都是你的?!接下来主持人宣布了比赛规则:
首先,比赛时间分为 �n 个时段,它又给出了很多小游戏,每个小游戏都必须在规定期限 ��ti 前完成。如果一个游戏没能在规定期限前完成,则要从奖励费 �m 元中扣去一部分钱 ��wi,��wi 为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。主持人只是想考考每个参赛者如何安排组织自己做游戏的顺序。作为参赛者,小伟很想赢得冠军,当然更想赢取最多的钱!注意:比赛绝对不会让参赛者赔钱!
输入格式
输入共四行。
第一行为 �m,表示一开始奖励给每位参赛者的钱;
第二行为 �n,表示有 �n 个小游戏;
第三行有 �n 个数,分别表示游戏 11 到 �n 的规定完成期限;
第四行有 �n 个数,分别表示游戏 11 到 �n 不能在规定期限前完成的扣款数。
输出格式
输出仅一行,表示小伟能赢取最多的钱。
样例
输入数据 1
10000 7 4 2 4 3 1 4 6 70 60 50 40 30 20 10
Copy
输出数据 1
9950
Copy
数据范围与提示
对于 100%100% 的数据,有 �≤500,1≤��≤�n≤500,1≤ti≤n。
家庭作业的简化版,懒得写了,套了家庭作业的皮改了一点东西
首先将问题看成是初始为0,最多可以挣多少钱的问题,求出最大的ans,然后拿总钱数sum-ans表示罚的钱,再拿奖金减去罚款即可
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int m;
int n;
int sum,ans;
struct app{
int t,q;
}a[505];
priority_queue<int,vector<int>,less<>>pq;
bool cmp(app x,app y){
if(x.t!=y.t)return x.t>y.t;
return x.q>y.q;
}
int main(){
cin>>m;
cin>>n;
int ppx=0;
for(int i=0;i<n;i++){
cin>>a[i].t;
ppx=max(ppx,a[i].t);
}
for(int i=0;i<n;i++){
cin>>a[i].q;
sum+=a[i].q;
}
sort(a,a+n,cmp);
int p=0;
for(int i=ppx;i>=1;i--){
while(i==a[p].t&&p<n){
pq.push(a[p].q);
p++;
}
if(pq.size()){
ans+=pq.top();
pq.pop();
}
}
cout<<m-(sum-ans);
return 0;
}
F. 「一本通 1.1 练习 1」数列极差
题目描述
佳佳的老师在黑板上写了一个由 �n 个正整数组成的数列,要求佳佳进行如下操作:每次擦去其中的两个数 �a 和 �b,然后在数列中加入一个数 �×�+1a×b+1,如此下去直至黑板上剩下一个数为止,在所有按这种操作方式最后得到的数中,最大的为 maxmax,最小的为 minmin, 则该数列的极差定义为 �=max−minM=max−min。
由于佳佳忙于准备期末考试,现请你帮助他,对于给定的数列,计算出相应的极差 �M。
输入格式
第一行为一个正整数 �n 表示正整数序列的长度;
在接下来的 �n 行中,每行输入一个正整数。
接下来的一行有一个 00,表示数据结束。输出格式
输出只有一行,为相应的极差 �d。
样例
输入数据 1
3 1 2 3 0
Copy
输出数据 1
2
Copy
数据范围与提示
对于全部数据,0≤�≤500000≤n≤50000,保证所有数据计算均在 3232 位有符号整数范围内。
这个就不需要太多思维了,直接暴力就行
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n;
priority_queue<ll,vector<ll>,greater<>>pq;
priority_queue<ll,vector<ll>,less<>>pq1;
int main(){
cin>>n;
for(int i=0;i<n;i++){
ll x;
cin>>x;
pq.push(x);
pq1.push(x);
}
while(pq.size()>1){//每次算最小的两个数
ll x=pq.top();
pq.pop();
ll y=pq.top();
pq.pop();
pq.push(x*y+1);
}
while(pq1.size()>1){//每次算最大的两个数
ll x=pq1.top();
pq1.pop();
ll y=pq1.top();
pq1.pop();
pq1.push(x*y+1);
}
cout<<abs(pq.top()-pq1.top());
return 0;
}
G. 「一本通 1.1 练习 2」数列分段
题目描述
对于给定的一个长度为 �N 的正整数数列 ��Ai,现要将其分成连续的若干段,并且每段和不超过 �M(可以等于 �M),问最少能将其分成多少段使得满足要求。
输入格式
第一行包含两个正整数 �,�N,M,表示了数列 ��Ai 的长度与每段和的最大值;
第二行包含 �N 个空格隔开的非负整数 ��Ai。
输出格式
输出文件仅包含一个正整数,输出最少划分的段数。
样例
输入数据 1
5 6 4 2 4 5 1
Copy
输出数据 1
3
Copy
数据范围与提示
对于 20%20% 的数据,有�≤10N≤10;
对于 40%40% 的数据,有�≤1000N≤1000;
对于 100%100% 的数据,有 �≤105,�≤109N≤105,M≤109,�M 大于所有数的最大值。
按照题意模拟就行
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,cnt=1;
ll a[100005];
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)cin>>a[i];
ll p=0;
for(int i=0;i<n;i++){
p+=a[i];
if(p>m){
cnt++;
p=a[i];
}
}
cout<<cnt;
return 0;
}
H. 「一本通 1.1 练习 3」线段
题目描述
数轴上有 �n 条线段,选取其中 �k 条线段使得这 �k 条线段两两没有重合部分,问 �k 最大为多少。
输入格式
第一行为一个正整数 �n;
在接下来的 �n 行中,每行有 22 个数 ��,��ai,bi,描述每条线段。
输出格式
输出一个整数,为 �k 的最大值。
样例
输入数据 1
3 0 2 2 4 1 3
Copy
输出数据 1
2
Copy
数据范围与提示
对于 20%20% 的数据,�≤10n≤10;
对于 50%50% 的数据,�≤103n≤103;
对于 70%70% 的数据,�≤105n≤105;
对于 100%100% 的数据,�≤106,n≤106, 0≤��<��≤1060≤ai<bi≤106。
是不是感觉写过?没错就是同类型题
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,cnt,p;
struct app{
int l,r;
}a[1000005];
bool cmp(app x,app y){//右端点排序
return x.r<y.r;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].l>>a[i].r;
}
sort(a,a+n,cmp);
for(int i=1;i<n;i++){
if(a[i].l>=a[p].r){
cnt++;
p=i;
}
}
cout<<cnt+1;
return 0;
}
I. 「一本通 1.1 练习 4」家庭作业
题目描述
老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为 1010,要求在 66 天内交,那么要想拿到这 1010 学分,就必须在第 66 天结束前交。
每个作业的完成时间都是只有一天。例如,假设有 7 次作业的学分和完成时间如下:
作业号 期限 学分 11 11 66 22 77 33 33 22 44 11 55 22 44 66 55 77 66 11 最多可以获得 1515 学分,其中一个完成作业的次序为 2,6,3,1,7,5,42,6,3,1,7,5,4,注意可能还有其他方法。
你的任务就是找到一个完成作业的顺序获得最大学分。
输入格式
第一行一个整数 �N,表示作业的数量;
接下来 �N 行,每行包括两个整数,第一个整数表示作业的完成期限,第二个数表示该作业的学分。
输出格式
输出一个整数表示可以获得的最大学分。保证答案不超过
C/C++
的int
范围。样例
输入数据 1
7 1 6 1 7 3 2 3 1 2 4 2 5 6 1
Copy
输出数据 1
15
Copy
数据范围与提示
对于 20%20% 的数据,�≤103N≤103;
对于 40%40% 的数据,�≤104N≤104;
对于 60%60% 的数据,�≤105N≤105;
对于 100%100% 的数据,�≤106N≤106,作业的完成期限均小于 7×1057×105。
贪心思想O(n),从第一天枚举到最后一天太难判断了也不好选择,所以我们倒过来从最后一天开始往前面选
分两种情况
假如最后一天期限的数量到前一天的数量不冲突,直接都选即可
假设存在冲突的,就需要比较大小
所以开一个优先队列来记录大小即可
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n;
ll ans;
struct app{
int t,f;
}a[1000005];
priority_queue<int,vector<int>,less<>>pq;
bool cmp(app x,app y){//按照最大时间排序
if(x.t!=y.t)return x.t>y.t;
return x.f>y.f;
}
int main(){
cin>>n;
int ppx=0;
for(int i=0;i<n;i++){
cin>>a[i].t>>a[i].f;
ppx=max(ppx,a[i].t);//找到最后的时间期限
}
sort(a,a+n,cmp);
int p=0;
for(int i=ppx;i>=1;i--){//从最后一天枚举到第一天
while(i==a[p].t&&p<n){//只要存在期限等于i的都加进去,当然最多也就n个
pq.push(a[p].f);
p++;
}
if(pq.size()){//只要队列不为空,就把里面学分最大的拿到手
ans+=pq.top();
pq.pop();
}
}
cout<<ans;
return 0;
}
J. 「一本通 1.1 练习 5」钓鱼
题目描述
在一条水平路边,有 �n 个钓鱼湖,从左到右编号为 1,2,…,�1,2,…,n。佳佳有 �H 个小时的空余时间,他希望利用这个时间钓到更多的鱼。他从 11 出发,向右走,有选择的在一些湖边停留一定的时间(是 55 分钟的倍数)钓鱼。最后在某一个湖边结束钓鱼。佳佳从第 �i 个湖到第 �+1i+1 个湖需要走 5×��5×Ti 分钟路,还测出在第 �i 个湖停留,第一个 55 分钟可以钓到 ��Fi 条鱼,以后每再钓 55 分钟,可以钓到的鱼量减少 ��Di,若减少后的鱼量小于 00,则减少后的鱼量为 00 。为了简化问题,佳佳假定没有其他人钓鱼,也没有其他因素影响他钓到期望数量的鱼。请编程求出佳佳最多能钓鱼的数量。
输入格式
第一行一个整数 �n,表示湖的个数
第二行一个整数 �H,表示佳佳的空闲时间
第三行有 �n 个整数,依次表示每个湖第一个 55 分钟能钓到鱼的数量
第四行有 �n 个整数,依次表示以后的每5分钟钓鱼数量比前一个 55 分钟钓鱼数量减少的数量
第五行有 �−1n−1 个整数,��Ti 表示由第 �i 个湖到第 �+1i+1 个湖需要花 5×��5×Ti 分钟的路程
输出格式
输出只有一行,表示佳佳最多能钓鱼的数量。
样例
输入数据 1
3 1 4 5 6 1 2 1 1 2
Copy
输出数据 1
35
Copy
在第 11 个湖钓 1515 分钟,共钓得 4+3+2=94+3+2=9 条鱼;
在第 22 个湖钓 1010 分钟,共钓得 5+3=85+3=8条鱼;
在第 33 个湖钓 2020 分钟,共钓得 6+5+4+3=186+5+4+3=18 条鱼;
从第 11 个湖到第 22 个湖,从第 22 个湖到第 33 个湖,共用时间 1515 分钟,共得 3535 条鱼,并且这是最多的数量。
数据范围与提示
对于 100%100% 的数据,2≤�≤100,1≤�≤202≤n≤100,1≤H≤20。
贪心+优先队列
由于题目已经说了是从1开始,往右走,所以我们记录当他走到每个湖时能钓到鱼的最大数量,然后比较取出最大值即可
#include<bits/stdc++.h>
using namespace std;
int a[105], b[105], t[105];
priority_queue<pair<int,int>, vector<pair<int,int> >, less<>>pq;
int main() {
int n, m;
cin >> n >> m;
m = m * 60 / 5;//表示五分钟的个数
for (int i = 0; i < n; i++){
cin >> a[i];
}
for (int i = 0; i < n; i++){
cin >> b[i];
}
for(int i=1;i<n;i++){
cin>>t[i];
}
int ans = 0;
for (int i = 0; i < n; i++) {
m -= t[i];//走到第i个湖时减去走路所需的时间
if(m<=0)break;//没有操作数直接打破循环
int pp=m;
for(int j=0;j<=i;j++){if(a[i]>0)pq.push({a[j],b[j]});}//将几个湖的初始和减少量加入
int cnt=0;
while(pp--){
int t=pq.top().first;
int tt=pq.top().second;
cnt+=t;
pq.pop();
if(t-tt>0)pq.push({t-tt,tt});//还能减就加入队列
if(pq.empty())break;//队列空了
}
while(pq.size())pq.pop();//如果队列没空,就清空队列
ans=max(ans,cnt);
}
cout<<ans;
}