贪心
1422:【例题1】活动安排
将终点进行一个排序之后来进行贪心的计算
【题目描述】 设有nn个活动的集合E={1,2,…,n}E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动ii都有一个要求使用该资源的起始时间sisi和一个结束时间fifi,且si<fisi<fi。如果选择了活动ii,则它在半开时间区间[si,fi)[si,fi)内占用资源。若区间[si,fi)[si,fi)与区间[sj,fj)[sj,fj)不相交,则称活动ii与活动jj是相容的。也就是说,当si≥fjsi≥fj或sj≥fisj≥fi时,活动ii与活动jj相容。选择出由相互兼容的活动组成的最大集合。 【输入】 第11行一个整数n(n≤1000)n(n≤1000),接下来nn行,每行两个整数sisi和fifi。 【输出】 输出尽可能多的互相兼容的活动个数。 【输入样例】 4 1 3 4 6 2 5 1 7 【输出样例】 2
#include <bits/stdc++.h> using namespace std; const int N=1005; struct h{ int en; int be; }a[1005]; bool cmp(h a,h b) { return a.en<b.en; } int main() { int n; int sum=0; int endd=-1; cin>>n; for(int i=1;i<=n;i++) { cin>>a[i].be>>a[i].en; } sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) { if(a[i].be>=endd) { sum++; endd=a[i].en; } } cout<<sum<<endl; return 0; }
1423:【例题2】种树
同样也是将每一个区域的重点进行一个排序,先是从前往后看前面是否有重合的已经种了树的,如果该区域还没有达到要求的话,就从后面开始种树,因为尾部是最容易有重合的地方
【题目描述】 现在我们国家开展新农村建设,农村的住房建设纳入了统一规划,统一建设,政府要求每一住户门口种些树。门口路边的地区被分割成块,并被编号成1..N。每个部分为一个单位尺寸大小并最多可种一棵树。每个居民房子门前被指定了三个号码B,E,T。这三个数表示该居民想在B和E之间最少种T棵树。当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l。居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量,尽量较少政府的支出。 【输入】 第一行包含数据N,M,区域的个数(0<N≤30000),房子的数目(0<m≤5000); 下面的m行描述居民们的需要:B E T,0<B≤E≤30000,T≤E-B+1。 【输出】 输出一个数,为满足所有居民的要求,所需要种树的最少数量。 【输入样例】 9 4 3 5 2 1 4 2 4 6 2 8 9 2 【输出样例】 5
#include <bits/stdc++.h> using namespace std; const int N=30005,M=5005,inf=0x3f3f3f; struct line{ int s,e,num; }a[N]; int vis[N]; bool cmp(line x,line y){ return x.e<y.e; } int main() { int n,m,ans=0,k; cin>>n>>m; for(int i=1;i<=m;i++) cin>>a[i].s>>a[i].e>>a[i].num; sort(a+1,a+1+m,cmp); for(int i=1;i<=m;i++){ k=0; for(int j=a[i].s;j<=a[i].e;j++) if(vis[j]) k++;//记录已经种了树的区域 if(k>=a[i].num) continue;//已经满足此区域的要求 for(int j=a[i].e;j>=a[i].s;j--){//从尾巴是最有可能和下一个区域重合的 if(!vis[j]){ vis[j]=1;k++;ans++; if(k==a[i].num) break; } } } cout<<ans; return 0; }
1424:【例题3】喷水装置
【题目描述】 长 LL 米,宽 WW 米的草坪里装有 nn 个浇灌喷头。每个喷头都装在草坪中心线上(离两边各 W2W2 米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。 请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头? 【输入】 输入包含若干组测试数据。 第一行一个整数 TT 表示数据组数; 每组数据的第一行是整数 nn、LL 和 WW; 接下来的 nn 行,每行包含两个整数,给出一个喷头的位置和浇灌半径(上面的示意图是样例输入第一组数据所描述的情况)。 【输出】 对每组测试数据输出一个数字,表示要浇灌整块草坪所需喷头数目的最小值。如果所有喷头都打开也不能浇灌整块草坪,则输出 −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 【输出样例】 6 2 -1 【提示】 数据范围: 对于 100% 的数据,n≤15000。
其实有用到勾股定理,自己画图理解下,然后按照起点从小到大排序,先找在起点在草坪左端的,谁的右边最大,依次这样下去;如果没有就在草坪右边的就不行,最后求出的右端点小于草坪的长度也是不行
#include <bits/stdc++.h> using namespace std; const int N=20005,inf=0x3f3f3f; struct line{ double s,e; }a[N]; int n,l,w,x,r,cnt; bool cmp(line x,line y) { return x.s<y.s; } void init() { cnt=0; cin>>n>>l>>w; for(int i=1;i<=n;i++) { cin>>x>>r; if(r<=w/2) continue; cnt++; a[cnt].s=x-sqrt(r*r-w*w/4.0); a[cnt].e=x+sqrt(r*r-w*w/4.0); } } void solve() { double t=0; int ans=0,bj=1,i=1; while(t<l){ ans++; double s=t; for(;a[i].s<=s&&i<=cnt;i++)//找到能够覆盖左端点的最靠近右边的点 if(t<a[i].e) t=a[i].e; if(t==s&&s<l){ cout<<-1<<endl; bj=0;break; } } if(bj) cout<<ans<<endl; } int main() { int t;cin>>t; while(t--){ init(); sort(a+1,a+1+cnt,cmp); solve(); } return 0; }
1425:【例题4】加工生产调度
【题目描述】 某工厂收到了 nn 个产品的订单,这 nn 个产品分别在 A、B 两个车间加工,并且必须先在 A 车间加工后才可以到 B 车间加工。 某个产品 ii 在 A,B 两车间加工的时间分别为Ai,BiAi,Bi。怎样安排这 nn 个产品的加工顺序,才能使总的加工时间最短。 这里所说的加工时间是指:从开始加工第一个产品到最后所有的产品都已在 A,B 两车间加工完毕的时间。 【输入】 第一行仅—个数据 nn ,表示产品的数量; 接下来 nn 个数据是表示这 nn 个产品在 A 车间加工各自所要的时间; 最后的 nn 个数据是表示这 nn 个产品在 B 车间加工各自所要的时间。 【输出】 第一行一个数据,表示最少的加工时间; 第二行是一种最小加工时间的加工顺序。 【输入样例】 5 3 5 8 7 10 6 2 1 4 9 【输出样例】 34 1 5 4 2 3 【提示】 对于100%的数据, 0 < n < 10000,所有数值皆为整数。
Johnson算法:设N1为a<b的作业集合,N2为a>=b的作业集合,将N1的作业按a非降序排序,N2的作业按b非增序排序,则N1作业接N2作业构成最优顺序。
这个题目的思路:我们第一件物品加工b机器肯定会等,所以就让a机器开始加工的物品时间最短,而我们B机器最后加工完成肯定是A机器在等,所以让B机器最后加工完的时间尽可能短
#include <bits/stdc++.h> using namespace std; const int N=10005; int a[N],b[N],s[N],m[N],ans[N],n,k,t; void solve() { for(int i=1;i<=n;i++){m[i]=min(a[i],b[i]);s[i]=i;} for(int i=1;i<=n-1;i++) for(int j=i+1;j<=n;j++) if(m[i]>m[j]){swap(m[i],m[j]);swap(s[i],s[j]);} k=0;t=n+1;//k为A加工时间,t为B加工时间 for(int i=1;i<=n;i++) if(m[i]==a[s[i]]){k++;ans[k]=s[i];}//就是N1类的,a<b else{t--;ans[t]=s[i];} //这种就是N2的,从后往前来 k=0;t=0; for(int i=1;i<=n;i++) { k+=a[ans[i]]; if(t<k) t=k; t+=b[ans[i]]; } cout<<t<<endl;//B机器结束的时间是结束的时间 for(int i=1;i<=n;i++) cout<<ans[i]<<" "; cout<<endl; } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) cin>>b[i]; solve(); return 0; } /*把所有任务分为两类 1类ai<bi,2类ai>bi 至于ai=bi放在哪一类均可,无影响, 1类可按ai排序,ai小的靠前 2类按bi排序,bi小的靠后 安排后的结果一定是1类全部执行完再执行2类*/
1426:【例题5】智力大冲浪
【题目描述】 小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者m元。先不要太高兴!因为这些钱还不一定都是你的。接下来主持人宣布了比赛规则: 首先,比赛时间分为n个时段(n≤500),它又给出了很多小游戏,每个小游戏都必须在规定期限ti前完成(1≤ti≤n)。如果一个游戏没能在规定期限前完成,则要从奖励费m元中扣去一部分钱wi,wi为自然数,不同的游戏扣去的钱是不一样的。当然,每个游戏本身都很简单,保证每个参赛者都能在一个时段内完成,而且都必须从整时段开始。主持人只是想考考每个参赛者如何安排组织自己做游戏的顺序。作为参赛者,小伟很想赢得冠军,当然更想赢取最多的钱! 注意:比赛绝对不会让参赛者赔钱! 【输入】 输入共4行。 第一行为m,表示一开始奖励给每位参赛者的钱; 第二行为n,表示有n个小游戏; 第三行有n个数,分别表示游戏1~n的规定完成期限; 第四行有n个数,分别表示游戏1~n不能在规定期限前完成的扣款数。 【输出】 仅1行。表示小伟能赢取最多的钱。 【输入样例】 10000 7 4 2 4 3 1 4 6 70 60 50 40 30 20 10 【输出样例】 9950 【提示】 数据范围及提示: n≤500,1≤ti≤n
思路:
先按罚款数额从大到小快排;顺序处理每个任务,若能安排,则找一个最晚时间,否则放在最后的空位上。
#include <bits/stdc++.h> using namespace std; struct th { int a,b; }t[505]; int pd[505]; bool cmp(th x,th y) { return x.b>y.b; } int main() { int n,m,i,j; cin>>m; cin>>n; for(int i=1;i<=n;i++) { cin>>t[i].a; } for(int i=1;i<=n;i++) { cin>>t[i].b; } sort(t+1,t+1+n,cmp); for(i=1;i<=n;i++) { for(j=t[i].a;j>=1;j--) { if(pd[j]==0) { pd[j]=1; break; } } if(j==0) m-=t[i].b; } cout<<m<<endl; return 0; }
1427:数列极差
思路:从小到大来的话就是最大值,从大到小来就是最小值;例如 1 2 3 ,(1*2+1)*3+1=10;(3*2+1)*1+1=8
#include <bits/stdc++.h> using namespace std; int n,a[50005],ans,mod; priority_queue<int,vector<int>,greater<int> > q;//小根堆 bool cmp(int a,int b){ return a>b; } int findmax() { int temp; while(!q.empty()) { temp=q.top(); q.pop(); temp=(temp*q.top()+1)%mod; q.pop(); if(q.empty()) break; q.push(temp); } return temp; } int findmin() { sort(a+1,a+1+n,cmp); int ans=a[1]; for(int i=2;i<=n;i++) ans=(ans*a[i]+1)%mod; return ans; } int main() { cin>>n>>mod; for(int i=1;i<=n;i++) { cin>>a[i]; q.push(a[i]); } int maxx=findmax(); int minn=findmin(); cout<<(maxx-minn)%mod<<endl; return 0; }
1428:数列分段
#include <bits/stdc++.h> using namespace std; int a[100005]; int main() { int n,m,i,sum=0,ans=1; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } for(i=1;i<=n;i++) { if(sum+a[i]>m) { ans++; sum=0; } sum+=a[i]; } cout<<ans<<endl; return 0; }
1429:线段
思路:按着段尾从小到大进行排序,在来比较已经被选择的上一段的队尾和队首来进行比较就可以了
#include <bits/stdc++.h> using namespace std; struct line { int a,b; }l[1000005]; bool cmp(line x,line y) { return x.b<y.b; } int main() { int n,ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d %d",&l[i].a,&l[i].b); } sort(l+1,l+1+n,cmp);//按着段尾从小到大进行排序; int e=-1; for(int i=1;i<=n;i++) { if(l[i].a>=e) { ans++; e=l[i].b; } } printf("%d\n",ans); return 0; }
1430:家庭作业
其实还是有点难,是一个贪心加上并查集的题目:根据学分从大到小进行排序,然后是进行一些并查集的操作,就看上一个时间
#include<bits/stdc++.h> using namespace std; const int N=1000005; struct node{ int t,s; }stu[N]; int n,flag,fa[N]; bool cmp(node x,node y){ if(x.s==y.s) return x.t<y.t; return x.s>y.s; } int find(int x){ if(x<=0) return x; else if(x==fa[x]){ fa[x]=x-1; flag=1; return fa[x]; } else return fa[x]=find(fa[x]); } int main() { cin>>n; for(int i=1;i<=n;i++) scanf("%d %d",&stu[i].t,&stu[i].s); sort(stu+1,stu+1+n,cmp); for(int i=1;i<=n;i++) fa[i]=i; long long ans=0; for(int i=1;i<=n;i++){ flag=0; fa[stu[i].t]=find(stu[i].t); if(flag) ans+=stu[i].s; } printf("%d\n",ans); return 0; } /* 7 4 0 4 5 7 0 5 6 0 1 0 1 0 7 0 0 5 2 5 0 0 2 4 0 5 0 0 */
1431:钓鱼
这个题目要用优先队列来做,要用到重载函数,主要的思路其实还是去枚举每个鱼塘作为最后一个鱼塘,那么根据贪心的思想,这个题目是肯定不会回头的,那么确定最后一个鱼塘之后,就按着最大的来,这个顺序不是最终的顺序,但是一定是把时间范围内能钓的最多的鱼都算进去了
比如说 4 (1) 5(2) 6(1) 走路需要3个5分钟,一共12个5分钟,钓鱼的时间为9个五分钟
1:6 还剩 4 5 5 2:5 4 5 4 3 5 4 3 4 ...这个不是钓鱼顺序
#include <bits/stdc++.h> using namespace std; const int N=10005,inf=0x3f3f3f; struct node{ int num,lose; bool operator <(node x)const{ return num<x.num; } }a[N]; int t[N],n,h,ans=0; priority_queue<node> q; int main() { cin>>n>>h; h*=12;//以5分钟为界限 for(int i=1;i<=n;i++){ cin>>a[i].num;} for(int i=1;i<=n;i++){cin>>a[i].lose;} for(int i=1;i<=n-1;i++) cin>>t[i]; for(int i=1;i<=n;i++){//枚举每个鱼塘作为最后一个 h-=t[i-1]; int now=0; while(!q.empty()) q.pop(); //初始化 for(int j=1;j<=i;j++) q.push(a[j]);//把以i为最后一个鱼塘的所有鱼塘加入 for(int j=1;j<=h;j++) { node s=q.top(); cout<<s.num<<endl; if(s.num>0) now+=s.num; s.num-=s.lose; q.pop(); q.push(s); } ans=max(now,ans); } cout<<ans<<endl; return 0; }
1432:糖果传递
这个题目是一个数学的推导题:我们先求出最终每个人的平均值avr,直接给的就只有旁边两个人,我们统一一下来进行假设,给左边,从右边拿
avr=a[1]-x1+x2 x2=avr-a[1]+x1 x2是右边给的,那么对于右边的小朋友来说,就是给左边的,所以
avr=a[2]-x2+x3 x3=avr-a[2]+x2=avr-a[2]+avr-a[1]+x1
...
avr=a[n]-xn+x1 无用
用一个数组c[]表示 a[]-avr
那么 x2=x1-c[1] x3=x1-c[2]...c[2]=c[1]+a[]-avr;
最后就是求x1+x2+x3+x4+...+xn
又因为:x1+x1-c1+x2-c2+...+xn-1-cn-1
我们希望Xi的绝对值之和尽量小,即|X1| + |X1-C1| + |X1-C2| + ……+ |X1-Cn-1|要尽量小。注意到|X1-Ci|的几何意义是数轴上的点X1到Ci的距离,所以问题变成了:给定数轴上的n个点,找出一个到他们的距离之和尽量小的点,而这个点就是这些数中的中位数
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1000005,inf=0x3f3f3f; ll a[N],c[N],ans=0,n,sum=0,mid,avr; int main() { cin>>n; for(int i=1;i<=n;i++){scanf("%lld",&a[i]);sum+=a[i];} avr=sum/n; for(int i=1;i<=n;i++){c[i]=c[i-1]+a[i]-avr;} sort(c,c+n); if(n&1){ mid=c[n/2]; } else{ mid=(c[(n-1)/2]+c[(n-1)/2+1])/2; }//找中位数 for(int i=0;i<n;i++) ans+=abs(c[i]-mid); printf("%lld\n",ans); return 0; }