目录
百炼 4110:圣诞老人的礼物-Santa Clau’s Gifts
二分
01:派
描述
我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。
我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。
请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。
输入
第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。
输出
输出每个人能得到的最大的派的体积,精确到小数点后三位。
样例输入
3 3
4 3 3
样例输出
25.133
题意:给定派的数目和朋友的数目,把给定的这些派一定要分割成大小相同然后分给朋友,同时还要留给自己一个
所以分派的人数要是朋友人数加1,求最大化的最小体积,
首先先求派的体积,最小分派是0,L=0,最大分派就是R=a[n-1],
利用二分求派的体积,然后判断这些派是否能够分给这些人。
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
double pi=acos(-1.0);//圆周率的计算公式
int n,f;
double a[10010];
bool check(double v)
{
int cnt=0;
for(int i=0;i<n;i++)
{
cnt=cnt+a[i]/v;
}
if(cnt>=f+1)
return true;
else
return false;
}
int main()
{
scanf("%d%d",&n,&f);
int ans;
for(int i=0;i<n;i++)
{
scanf("%lf",&a[i]);
}
sort(a,a+n);
for(int i=0;i<n;i++)
{
a[i]=a[i]*a[i]*pi;
}
double L=0,R=a[n-1];
while(R-L>=1e-6) //精度易错
{
double mid=L+(R-L)/2;
if(check(mid))
{
L=mid;
}
else
{
R=mid;
}
}
printf("%.3lf\n",L);
return 0;
}
注意:
1.π的取值公式
2.二分循环的精度
3.要学会怎样二分,把什么二分,利用二分法求答案目前要弄清这两个问题。
4.做法比较独特,有许多地方需要借鉴 https://www.cnblogs.com/zzyh/p/6638583.html
02:河中跳房子
描述
每年奶牛们都要举办各种特殊版本的跳房子比赛,包括在河里从一个岩石跳到另一个岩石。这项激动人心的活动在一条长长的笔直河道中进行,在起点和离起点L远 (1 ≤ L≤ 1,000,000,000) 的终点处均有一个岩石。在起点和终点之间,有N (0 ≤ N ≤ 50,000) 个岩石,每个岩石与起点的距离分别为Di (0 < Di < L)。
在比赛过程中,奶牛轮流从起点出发,尝试到达终点,每一步只能从一个岩石跳到另一个岩石。当然,实力不济的奶牛是没有办法完成目标的。
农夫约翰为他的奶牛们感到自豪并且年年都观看了这项比赛。但随着时间的推移,看着其他农夫的胆小奶牛们在相距很近的岩石之间缓慢前行,他感到非常厌烦。他计划移走一些岩石,使得从起点到终点的过程中,最短的跳跃距离最长。他可以移走除起点和终点外的至多M (0 ≤ M ≤ N) 个岩石。
请帮助约翰确定移走这些岩石后,最长可能的最短跳跃距离是多少?
输入
第一行包含三个整数L, N, M,相邻两个整数之间用单个空格隔开。
接下来N行,每行一个整数,表示每个岩石与起点的距离。岩石按与起点距离从近到远给出,且不会有两个岩石出现在同一个位置。
输出
一个整数,最长可能的最短跳跃距离。
样例输入
25 5 2
2
11
14
17
21
样例输出
4
提示
在移除位于2和14的两个岩石之后,最短跳跃距离为4(从17到21或从21到25)。
题意:移石头,两个岩石之间的距离为D,从一个岩石出发跳到另一个岩石,
找最大化的最短距离。从一个石头i开始,跳到岩石j,i到j之间距离如果大于mid,则把i与j之间的(j-i-1)个石头全部移走
否则,j继续++如果移走的石头小于等于m,证明mid太小了,需要大一点的mid
如果移走的石头大于m,证明mid太大了,需要小一点的mid
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
int l,n,m;
int a[50010];
int main()
{
scanf("%d%d%d",&l,&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
a[0]=0;
a[n+1]=l;
int L=0,R=l+1;
while(R-L>1)
{
int mid=L+(R-L)/2;
int p=0;
int i=0;
while(i<=n)
{
int j=i+1;
while(j<=n+1 && a[j]-a[i]<mid)
{
j++;
}
p+=j-i-1;
i=j;
}
if(p<=m)
{
L=mid;
// cout<<"L="<<L<<endl;
}
else
{
R=mid;
// cout<<"R="<<R<<endl;
}
}
printf("%d\n",L);
return 0;
}
注意:
1. i与j之间移动的石头的个数为j-i-1;
https://blog.csdn.net/LMengi000/article/details/81451748 好斗的奶牛变形做法
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
int l,n,m;
int a[50010];
bool check(int d)
{
int cnt=1;
int preston=0;
for(int i=1;i<=n+1;i++)
{
if(a[i]-a[preston]>=d)
{
cnt++;
preston=i;
}
if(cnt>=n+2-m)
return true;
}
return false;
}
int main()
{
int ans;
scanf("%d%d%d",&l,&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
a[0]=0;
a[n+1]=l;
int L=0,R=l;
while(L<=R)
{
int mid=L+(R-L)/2;
//cout<<"mid="<<mid<<endl;
if(check(mid))
{
ans=mid;
L=mid+1;
//cout<<"L="<<L<<endl;
}
else
{
R=mid-1;
//cout<<"R="<<R<<endl;
}
}
printf("%d\n",ans);
return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5e4+10;
const int inf=1e9;
int L,N,M,D[maxn];
bool C(int d){
int last,crt;
for(int i=0;i<M;i++){
if(i==0){
crt=0;
while(crt<N&&D[crt]<d)
crt++;
}else{
crt=last+1;
while(crt<N&&D[crt]-D[last]<d)
crt++;
}
if(crt==N) return false;
last=crt;
}
while(crt<N&&L-D[last]<d)
crt++;
if(crt==N) return false;
return true;
}
void solve(){
sort(D,D+N);
int lb=1,ub=inf;
while(ub-lb>1){
int mid=(lb+ub)/2;
if(C(mid)) lb=mid;
else ub=mid;
}
printf("%d\n",lb);
}
int main(){
scanf("%d%d%d",&L,&N,&M);
for(int i=0;i<N;i++)
scanf("%d",&D[i]);
M=N-M;
solve();
return 0;
}
03:矩形分割
描述
平面上有一个大矩形,其左下角坐标(0,0),右上角坐标(R,R)。大矩形内部包含一些小矩形,小矩形都平行于坐标轴且互不重叠。所有矩形的顶点都是整点。要求画一根平行于y轴的直线x=k(k是整数) ,使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。并且,要使得大矩形在直线左边的的面积尽可能大。注意:若直线穿过一个小矩形,将会把它切成两个部分,分属左右两侧。
输入
第一行是整数R,表示大矩形的右上角坐标是(R,R) (1 <= R <= 1,000,000)。
接下来的一行是整数N,表示一共有N个小矩形(0 < N <= 10000)。
再接下来有N 行。每行有4个整数,L,T, W 和 H, 表示有一个小矩形的左上角坐标是(L,T),宽度是W,高度是H (0<=L,T <= R, 0 < W,H <= R). 小矩形不会有位于大矩形之外的部分。
输出
输出整数n,表示答案应该是直线 x=n。 如果必要的话,x=R也可以是答案。
样例输入
1000
2
1 1 2 1
5 1 2 1
样例输出
5
题意:把坐标轴从0到R二分,当二分到某一个x值时,使得1.左边小矩形的面积大于等于右边小矩形的面积,且且两边面积之差最小,这一步在二分完成之后,就已经完成了。2.使得大矩形在直线左边的的面积尽可能大,二分完成之后,最优解有有left和right,目前自己有一个疑惑,就是在取最优解保证大矩形在直线左边的面积尽可能大,之前自己做的时候,直接采用了差的形式,但是直接做差比较会很不准确,想了好久总是走不出这个误区。
自己的想法和网上这份代码相似度95%,但是依旧不能理解它选择最优解的过程,用绝对值能保证左边小矩形的面积大于等于右边小矩形的面积,且且两边面积之差最小,但是不能使得大矩形在直线左边的的面积尽可能大
相似度极高代码::https://blog.csdn.net/zhhe0101/article/details/52794852
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
using namespace std;
long long int R,n;
long long int sum1,sum2;
long long int maxn,ans;
struct rect
{
long long int L,T,W,H;//分别是小矩形的左上端点 宽 高
long long int area;
long long int y;
}a[500100];
long long int S(int mid)
{
sum1=sum2=0;
for(int i=0;i<n;i++)
{
if(a[i].y<=mid)
{
sum1+=a[i].area;
}else if(a[i].L<mid && a[i].y>mid )
{
sum1+=(mid-a[i].L)*a[i].H;
sum2+=(a[i].y-mid)*a[i].H;
}
else
{
sum2+=a[i].area;
}
}
return sum1-sum2;
}
int main()
{
scanf("%d",&R);
scanf("%d",&n);
long long int maxn=-1;
for(int i=0;i<n;i++)
{
scanf("%lld%lld%lld%lld",&a[i].L,&a[i].T,&a[i].W,&a[i].H);
a[i].area=a[i].W*a[i].H;//小矩形的面积
a[i].y=a[i].L+a[i].W;//小矩形最右端的端点横坐标
if(a[i].L+a[i].W>maxn)
maxn=a[i].L+a[i].W;
}
int left=0,right=R;
while(right-left>1)
{
int mid=left+(right-left)/2;
if(S(mid)<=0)//使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。这样就会出现left边界和right边界
{
left=mid;
}
else
{
right=mid;
}
}
//使得大矩形在直线左边的的面积尽可能大
long long int p1=S(left);//以left为分界线,左右的面积之差
long long int p2=S(right);//以right为分界线,左右的面积之差
if(abs(p1)>=abs(p2))
{
ans=right;
}
else if(abs(p1)<abs(p2))
{
ans=left;
}
if(ans==maxn)
{
ans=R;
}
/*
if(p1<p2)
{
if(p1>=0)
ans=left;
else
ans=right;
}else if(p1>p2)
{
if(p2>=0)
ans=right;
else
ans=left;
}
if(ans==maxn)
{
ans=R;
}
*/
printf("%lld\n",ans);
return 0;
}
分治
07:求排列的逆序数
描述
在Internet上的搜索引擎经常需要对信息进行比较,比如可以通过某个人对一些事物的排名来估计他(或她)对各种不同信息的兴趣,从而实现个性化的服务。
对于不同的排名结果可以用逆序来评价它们之间的差异。考虑1,2,…,n的排列i1,i2,…,in,如果其中存在j,k,满足 j < k 且 ij > ik, 那么就称(ij,ik)是这个排列的一个逆序。
一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。显然,由1,2,…,n 构成的所有n!个排列中,最小的逆序数是0,对应的排列就是1,2,…,n;最大的逆序数是n(n-1)/2,对应的排列就是n,(n-1),…,2,1。逆序数越大的排列与原始排列的差异度就越大。
现给定1,2,…,n的一个排列,求它的逆序数。
输入
第一行是一个整数n,表示该排列有n个数(n <= 100000)。
第二行是n个不同的正整数,之间以空格隔开,表示该排列。
输出
输出该排列的逆序数。
样例输入
6
2 6 3 4 5 1
样例输出
8
提示
1. 利用二分归并排序算法(分治);
2. 注意结果可能超过int的范围,需要用long long存储。
基于归并排序
利用分治思想,来求逆序数
样例 2 6 3 4 5 1
1.利用二分,把数组分成两份,左边的逆序数+右边的逆序数
2.左边取一个数,右边取一个数,这样组成的逆序数相加
2 6 3 4 5 1 分成两组
一 2 6 3;又二分成 2 6 ;又二分成 2; 左边数组又分成两份
数组一:<2> <2,6> <2,6,3>
数组二:<6> <3>
1.2<6,升序,不是逆序数 ;
2.2<3,升序,不是逆序数; 6>3,降序,是逆序数,并且需要排序,
排完顺序之后,是 2 3 6 逆序数是1
二 4 5 1;又二分成 4 5 ;又二分成 4; 右边数组又分成两份
数组一:<4> <4,5> <1,4,5>
数组二:<5> <1>
1.4<5,升序,不是逆序数 ;
2.4>1,降序,是逆序数,并且需要排序;
5>1,降序,是逆序数,并且需要排序;
3.排完顺序之后,是 1 4 5 逆序数是2
上面过程之后 数组一 2 3 6 数组二 1 4 5 再综合排序
p1 m p2
2>1,所以2之后的数据都能与1构成逆序数 <2,1><3,1><6,1>
6>4,所以6之后(6是最后一个元素)的数据都能与1构成逆序数 <6,4>
6>5,所以6之后(6是最后一个元素)的数据都能与1构成逆序数 <6,5>
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
const long long int maxn=1e5+10;
long long a[maxn];
long long b[maxn];
int n;
long long int cnt;
void Merge(long long int a[],int s,int m,int e,long long int tmp[])
{
int pb=0;
int p1=s,p2=m+1;
while(p1<=m && p2<=e)
{
if(a[p1]<a[p2])
{
tmp[pb++]=a[p1++];
}
else
{
cnt=cnt+(m-p1+1);
tmp[pb++]=a[p2++];
}
}
while(p1<=m)
{
tmp[pb++]=a[p1++];
}
while(p2<=e)
{
tmp[pb++]=a[p2++];
}
for(int i=0;i<e-s+1;i++)
a[s+i]=tmp[i];
}
void MergeSort(long long int a[],int s,int e,long long int tmp[])
{
if(s<e)
{
int m=s+(e-s)/2;
MergeSort(a,s,m,tmp);
MergeSort(a,m+1,e,tmp);
Merge(a,s,m,e,tmp);
}
}
int main()
{
scanf("%d",&n);
cnt=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
MergeSort(a,0,n-1,b);
printf("%lld\n",cnt);
return 0;
}
注意:
1.要注意细节,知道数组起始和结束,这之间元素的个数是 结束-起始+1;
2.分治的思想是要把所求的问题分块,一块一块来求,然后再归并到整个,所以一开始分块的时候,是要把每一块都分到最小的。
3.cnt的个数是(e-s+1)个,这个要注意+1操作
08:输出前k大的数
描述
给定一个数组,统计前k大的数并且把这k个数从大到小输出。
输入
第一行包含一个整数n,表示数组的大小。n < 100000。
第二行包含n个整数,表示数组的元素,整数之间以一个空格分开。每个整数的绝对值不超过100000000。
第三行包含一个整数k。k < n。
输出
从大到小输出前k大的数,每个数一行。
样例输入
10
4 5 6 9 8 7 1 2 3 0
5
样例输出
9
8
7
6
5
基于快速排序
本题目要求用分治法做这道题目,目前没有用排序时排最少个数的方法把这个题目做出来,总是会出现死循环
AC代码,但没有体现分治----减少排序元素的个数
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
const int maxn=1e5+10;
int n;
int a[maxn];
int k;
void swap(int &a,int &b)
{
int temp=a;
a=b;
b=temp;
}
/*
void QuickSort(int a[],int s,int e,int k)
{
if(s>=e)
return ;
int p=a[s];
int i=s,j=e;
while(i!=j)
{
while(i<j && a[j]>=p)
--j;
swap(a[i],a[j]);
while(i<j && a[i]<=p)
++i;
swap(a[i],a[j]);
}
int num=e-i+1;
if(num==k)
return ;
if(num>k)
QuickSort(a,i+1,e,k);
if(num<k)
QuickSort(a,s,i-1,k-num);
}*/
void arrangeRight(int *a,int s,int e,int k)
{ if(s>=e)
return ;
int t=a[s];
int i=s,j=e;
while(i!=j){
while(i<j&&t<=a[j])//不要漏掉等号
--j;
swap(a[i],a[j]);
while(i<j&&t>=a[i])
++i;
swap(a[i],a[j]);
}
arrangeRight(a,s,i-1,k);
arrangeRight(a,i+1,e,k);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
scanf("%d",&k);
arrangeRight(a,0,n-1,k);
/* for(int i=0;i<n;i++)
{
printf("%d\n",a[i]);
}*/
for(int i=n-1;k>0;i--)
{
printf("%d\n",a[i]);
k--;
}
return 0;
}
注意:
1.再排序的时,函数arrangeRight里面,判断--j和++i时,while循环条件里面等号不要漏掉
2.排序的起始位置和终止位置,快速排序与归并排序区分开
贪心
百炼 4110:圣诞老人的礼物-Santa Clau’s Gifts
描述
圣诞节来临了,在城市A中圣诞老人准备分发糖果,现在有多箱不同的糖果,每箱糖果有自己的价值和重量,每箱糖果都可以拆分成任意散装组合带走。圣诞老人的驯鹿最多只能承受一定重量的糖果,请问圣诞老人最多能带走多大价值的糖果。
输入
第一行由两个部分组成,分别为糖果箱数正整数n(1 <= n <= 100),驯鹿能承受的最大重量正整数w(0 < w < 10000),两个数用空格隔开。其余n行每行对应一箱糖果,由两部分组成,分别为一箱糖果的价值正整数v和重量正整数w,中间用空格隔开。
输出
输出圣诞老人能带走的糖果的最大总价值,保留1位小数。输出为一行,以换行符结束。
样例输入
4 15
100 4
412 8
266 7
591 2
样例输出
1193.0
题意:雪橇承载的重量有限,要保证承载的价值最大,就要求出,在同等重量下,谁的价值大就要先把谁装进去,所以就要求出价值/重量这一比值,按照从大到小的顺序排序。但是当雪橇承载的重量不足以再次承载整箱糖果时,就要把一箱糖果拆开,能放发多少重量就放多少重量,然后再算出放剩下这些重量的价值。
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
const int maxn=1e4+10;
int n,w;
struct gift
{
int v;
int w;
double ave;
}candies[maxn];
int compare(struct gift a,struct gift b)
{
return a.ave>b.ave;
}
int main()
{
scanf("%d%d",&n,&w);
for(int i=0;i<n;i++)
{
scanf("%d%d",&candies[i].v,&candies[i].w);
candies[i].ave=(double)candies[i].v/(double)candies[i].w;
}
sort(candies,candies+n,compare);
int totalw=0;
double totalv=0;
for(int i=0;i<n;i++)
{
if(totalw+candies[i].w<=w)
{
totalw+=candies[i].w;
totalv+=candies[i].v;
}
else//需要注意
{
totalv+=candies[i].v*double(w-totalw)/candies[i].w;
break;
}
}
printf("%.1lf\n",totalv);
return 0;
}
注意:
1.当不足以放下正像糖果的时候,要把糖果拆开,装入剩余的重量,使得雪橇承载的重量等于雪橇能够承载的重量,然后还要计算装入这些糖果的价值。
百炼 4151:电影节
描述
大学生电影节在北大举办! 这天,在北大各地放了多部电影,给定每部电影的放映时间区间,区间重叠的电影不可能同时看(端点可以重合),问李雷最多可以看多少部电影。
输入
多组数据。每组数据开头是n(n<=100),表示共n场电影。
接下来n行,每行两个整数(0到1000之间),表示一场电影的放映区间
n=0则数据结束
输出
对每组数据输出最多能看几部电影
样例输入
8
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
0
样例输出
3
来源
Guo Wei
题意:贪心的解法,可以自己出几组数据试验一下, 如果要保证看的电影最多的话,就要按照电影结束的时间最早,然后再接着看下一场电影,这样就能保证看的电影的次数最多,所以要对电影的结束时间从小到大排序,假设第一场电影已经看过了,然后从第一场电影的结束时间往后继续找符合条件的电影。
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
int n,cnt=0;
struct film
{
int s;
int e;
}film[1100];
int compare(struct film a,struct film b)
{
return a.e<b.e;
}
int main()
{
while(1)
{
scanf("%d",&n);
if(n==0)
break;
for(int i=0;i<n;i++)
{
scanf("%d%d",&film[i].s,&film[i].e);
}
sort(film,film+n,compare);
cnt=1;
int last=0;
for(int i=1;i<n;i++)
{
if(film[i].s>=film[last].e)
{
cnt++;
last=i;
}
}
printf("%d\n",cnt);
}
return 0;
}
注意:
1. 用last变量来记录当前电影,在找到合适电影的时候,就要更换last的值。
POJ 3190 Stall Reservations
Description
Oh those picky N (1 <= N <= 50,000) cows! They are so picky that each one will only be milked over some precise time interval A..B (1 <= A <= B <= 1,000,000), which includes both times A and B. Obviously, FJ must create a reservation system to determine which stall each cow can be assigned for her milking time. Of course, no cow will share such a private moment with other cows.
Help FJ by determining:
- The minimum number of stalls required in the barn so that each cow can have her private milking period
- An assignment of cows to these stalls over time
Many answers are correct for each test dataset; a program will grade your answer.
Input
Line 1: A single integer, N
Lines 2..N+1: Line i+1 describes cow i's milking interval with two space-separated integers.
Output
Line 1: The minimum number of stalls the barn must have.
Lines 2..N+1: Line i+1 describes the stall to which cow i will be assigned for her milking period.
Sample Input
5
1 10
2 4
3 6
5 8
4 7
Sample Output
4
1
2
3
2
4
Hint
Explanation of the sample:
Here's a graphical schedule for this output:
Time 1 2 3 4 5 6 7 8 9 10
Stall 1 c1>>>>>>>>>>>>>>>>>>>>>>>>>>>
Stall 2 .. c2>>>>>> c4>>>>>>>>> .. ..
Stall 3 .. .. c3>>>>>>>>> .. .. .. ..
Stall 4 .. .. .. c5>>>>>>>>> .. .. ..
Other outputs using the same number of stalls are possible.
Source
题意:n头牛要挤奶,每头牛都有一个挤奶的时间区间,牛需要在牲畜栏里挤奶,一个牲畜栏只能容纳一头牛,问至少需要多少个牲畜栏,才能完成全部挤奶工作,以及每头牛都放在哪个牲畜栏里。
两头牛的挤奶时间区间哪怕是端点重合也是不可以的。
1.牛到了要挤奶的时间,就要去牲畜栏里去挤奶,因此牛的挤奶顺序是牛的开始挤奶时间决定的,谁开始的时间早,谁就先去挤奶,因此,牛的排序是按照牛的开始时间按照从小到大排的。
2.牲畜栏的使用时间是在不断变化的,牛挤奶结束,牲畜栏就使用完毕,就可以再给下一个要挤奶的奶牛使用;按照优先队列用法,将牲畜栏的使用结束时间按照从大到小排序,时间最早的优先级最高,放在队头。
3.再进行的过程就是奶牛选牲畜栏进行挤奶了,定义total来表示建立的牲畜栏的数目。
(1)如果牲畜栏为空,则需要建立一个牲畜栏,用pos数组来记录编号为0的奶牛挤奶的牲畜栏的编号,将奶牛挤奶结束的时间和牲畜栏编号传给Stall,以便排序。
(2)如果牲畜栏部位空,则奶牛挤奶就需要判断当前是否有牲畜栏可以使用,如果有牲畜栏可以使用,就直接取队头牲畜栏,将奶牛的挤奶的牲畜栏编号赋值给pos,再将奶牛的挤奶结束时间和牲畜栏编号进行排序。
(3)如果奶牛没有牲畜栏可以去挤奶,就需要新建一个牲畜栏,这样total就需要+1,记录奶牛挤奶的牲畜栏编号,然后再对奶牛的挤奶结束时间和牲畜栏编号进行排序。
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
using namespace std;
struct Cow
{
int a,b;
int No;
bool operator<(const Cow & c) const
{
return a<c.a;//
}
}cows[50100];
int pos[50100];
struct Stall
{
int end;
int No;
bool operator<(const Stall & s)const
{
return end>s.end;//
}
Stall(int e,int n):end(e),No(n){ }
/* 等价于 意义在于修改牲畜栏的结束时间和牲畜栏的编号
Stall s;
s.end=cows[i].b;
s.No=total;
*/
};
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d%d",&cows[i].a,&cows[i].b);//
cows[i].No=i;//
}
sort(cows,cows+n);
int total = 0; //表示新建的牲畜栏
priority_queue<Stall>pq; //优先队列处理牲畜栏,
for(int i=0;i<n;++i)
{
if(pq.empty())//表示没有牲畜栏,奶牛还没有被处理
{
++total;//建立一个牲畜栏
pq.push(Stall(cows[i].b,total));//牲畜栏的结束时间和牲畜栏的编号
pos[cows[i].No]=total;//编号为No的奶牛进入了total这个牲畜栏里
}
else
{
Stall st =pq.top();//取队头,结束时间最早的牲畜栏
if(st.end<cows[i].a)//找到空的牲畜栏
{
pq.pop();
pos[cows[i].No]=st.No;//记录奶牛放入的牲畜栏的编号
pq.push(Stall(cows[i].b,st.No));//有新的奶牛放入,就要修改牲畜栏的结束时间,给牲畜栏重新编号。
}
else//没有找到空的牲畜栏,就要新建一个牲畜栏
{
++total;//新建牲畜栏
pq.push(Stall(cows[i].b,total)); //修改牲畜栏的结束时间和编号
pos[cows[i].No]=total;//记录刚刚奶牛放入的牲畜栏的编号
}
}
}
printf("%d\n",total);
for(int i=0;i<n;++i)
{
printf("%d\n",pos[i]);
}
return 0;
}
注意:
1.不要忘记对奶牛的编号进行赋初值 cows[i].No
2.不要忘记对奶牛的初始时间进行从小到大进行排序
3.要把奶牛挤奶结束的时间和牲畜栏的编号进行重新赋值