UVALive-4850 Installations
题目大意:
有若干任务, 每个任务有完成消耗的时间和截止的时间.
如果完成某个任务的时刻是t, 截止时间是d, 那么罚时就是max( 0 , t-d ), 现在求罚时最大的任务和次大的任务的罚时和最少是多少.
首先很快就可以想到一个贪心的方法: 按照时间限制d排序, 相同就按照安装时间s排序, 都从小到大, 扫一遍就可以了.
然后就会发现过不了样例.
那么我们来研究一下这个样例:
6
1 7
4 7
2 4
2 15
3 5
3 8
6个任务, 每一行前一个数是s, 表示完成消耗时间, 后一个数是d, 表示时间限制, 每个任务用Ji表示, 这个样例还有下面这个图来辅助解释:
它按照贪心的方法放的话最大和次大应该是J6和J2, 结果是8, 但是正确答案是J2和J6, 答案是7.
我们发现其实就是把J6和J2换了位置, 或者说, 是把J2放到了J6的后面.
但是只交换最大和次大明显是错的, 而把后面的放到前面来只会让大的更大, 那么我们唯一可能的方法就是把前面的放到后面来.
记一下罚时最大和次大的是哪两个任务, 然后去这两个靠后的一个的位置记为cd, 然后我们枚举cd前面的某一个移动到cd的后面, O( n )扫一遍计算答案, 更新答案, 不断移动. 再和完全不移动直接贪心的答案取min, 这样就可以得到答案了.
为什么是对的? 首先cd之后的部分是不受移动的影响的, 我们从前面取出来一个放到了cd的后面, 而在cd前面的要么截止时间靠前, 要么处理时间短, 放到cd后面只会成为新的最大值, 而旧的最大和次大其中一个会变成次大值, 就有可能会使答案变小( 答案是最大和次大的和 ).
为什么只移动一个? 因为把前面的放到cd后面, 结果变成最大, 如果再选一个绝对比原来的次大或者最大要大, 我们新增了两个更大的, 答案明显会更大, 所以只要选择一个交换.
这样复杂度就是O( n )枚举移动, O( n )计算每一种答案, 总复杂度O( n^2 ).
移动用链表实现.
代码如下:
//made by Crazy01
#include<bits/stdc++.h>
#define inf 1<<30
#define ll long long
#define db double
#define c233 cout<<"233"<<endl
#define mem(s) memset(s,0,sizeof(s))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int N=550;
using namespace std;
struct lll{
int tim,lim;
bool operator <(const lll &a)const{
return lim==a.lim?tim<a.tim:lim<a.lim;
}
}job[N];
int n,T,ans,cd,r[N];
inline int gi(){
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-48,ch=getchar();
return x*res;
}
void init(){
n=gi(); r[0]=1;
for(int i=1;i<=n;i++)
job[i].tim=gi(),job[i].lim=gi(),r[i]=i+1;
sort(job+1,job+1+n);
}
int calc(){
int t=0,max1=0,max2=0;
cd=0;
for(int i=r[0];i<=n;i=r[i]){
t+=job[i].tim;
int tle=max(0,t-job[i].lim);
if(tle>max1)max2=max1,max1=tle,cd=i;
else if(tle>max2)max2=tle,cd=i;
}
return max1+max2;
}
void work(){
if(!ans){printf("%d\n",ans); return;}
for(int i=r[0],end=cd;i<end;i=r[i]){
r[i-1]=r[i]; r[i]=r[end]; r[end]=i;
ans=min(ans,calc());
r[end]=r[i]; r[i]=r[i-1]; r[i-1]=i;
}
printf("%d\n",ans);
}
int main(){
T=gi();
while(T--){
mem(job);
init();
ans=calc();
work();
}
return 0;
}