【题解】
仅按T1或T2从小到大修理显然是不正确的
可以发现:对于某个建筑集合,若它们都能被抢修,则按T2从小到大的顺序修理是最优的
证明:(相邻交换法)
对于建筑物(a1,b1)与(a2,b2),(a,b)代表(T1,T2)且T前+a<=b,设b1<=b2,可得下表:
实际修理时间 允许时间 实际修理时间 允许时间
1号在前:T前+a1 b1 2号在前:T前+a2 b2
T前+a1+a2 b2 T前+a1+a2 b1
若 2号在前时,1、2号都能修,则 T前+a1+a2 <= b1 <= b2,因此 1号在前时也能修两个
若 1号在前时,1、2号都能修,则只知道 T前+a1+a2 <= b2,不能保证 2号在前时也能修两个
(也可用此方法尝试证明 能否按T1从小到大的顺序修理 结果是无法证明)
结论:按T2乱序能全修的,按T2升序也必能全修; 反之则不一定
因此 按T2从小到大的顺序修理是最优的
产生贪心算法:
先按T2从小到大排序,从前往后决策
对于i号,若 a总 <= bi,则修复
否则,找出之前修复过的建筑中 耗时(a)最大的j号,若aj>ai(保证替换后结果更优),则不修j,改为修i
用大根堆找出 这个j号即可
#include<stdio.h>
#include<stdlib.h>
int a[150005]={0},b[150005]={0},heap[150005]={0};
int p=0;
void jh(int* a,int* b)
{
int t=*a;
*a=*b;
*b=t;
}
void kp(int low,int high)
{
int i=low,j=high,mid=b[(i+j)/2];
while(i<j)
{
while(b[i]<mid) i++;
while(b[j]>mid) j--;
if(i<=j)
{
jh(&a[i],&a[j]);
jh(&b[i],&b[j]);
i++;
j--;
}
}
if(j>low) kp(low,j);
if(i<high) kp(i,high);
}
void tj(int x)//添加元素
{
int i;
heap[++p]=x;
for(i=p;i!=1;i/=2)
{
if(heap[i]>heap[i/2]) jh(&heap[i],&heap[i/2]);
else return;
}
}
void sc()//删除堆顶
{
int i=1;
heap[1]=heap[p--];
while(i*2<=p)
{
i*=2;
if(i+1<=p&&heap[i]<heap[i+1]) i++;
if(heap[i]>heap[i/2]) jh(&heap[i],&heap[i/2]);
else return;
}
}
int main()
{
int n,i,t=0,ans=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d%d",&a[i],&b[i]);
kp(1,n);
for(i=1;i<=n;i++)
{
if(t+a[i]<=b[i])
{
t+=a[i];
tj(a[i]);
ans++;
}
else
if(p>0&&heap[1]>a[i])
{
t=t-heap[1]+a[i];
sc();
tj(a[i]);
}
}
printf("%d",ans);
return 0;
}