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;
}
posted @ 2017-10-17 20:38  穆忞千载  阅读(284)  评论(0编辑  收藏  举报