uva1467 - Installation 贪心+暴力

In the morning, service engineers in a telecom company receive a list of jobs which they must serve today. They install telephones, internet, ipTVs, etc and repair troubles with established facilities. A client requires a deadline when the requested job must be completed. But the engineers may not complete some jobs within their deadlines because of job overload. For each job, we consider, as a penalty of the engineer, the difference between the deadline and the completion time. It measures how long the job proceeds after its deadline. The problem is to find a schedule minimizing the sum of the penalties of the jobs with the two largest penalties.

A service engineer gets a list of jobs Ji with a serving time si and a deadline di. A job Ji needs time si, and if it is completed at time Ci, then the penalty of Ji is defined to be max{0, Ci - di}. For convenience, we assume that the time t when a job can be served is 0$ \le$t < $ \infty$ and si and di are given positive integers such that 0 < si$ \le$di. The goal is to find a schedule of jobs minimizing the sum of the penalties of the jobs with the two largest penalties.

For example, there are six jobs Ji with the pair (si, di) of the serving time si and the deadline di, i = 1,..., 6, where (s1, d1) = (1, 7), (s2, d2) = (4, 7), (s3, d3) = (2, 4), (s4, d4) = (2, 15), (s5, d5) = (3, 5), (s6, d6) = (3, 8). Then Figure 1 represents a schedule which minimizes the sum of the penalties of the jobs with the two largest penalties. The sum of the two largest penalties of an optimal schedule is that of the penalties of J2 and J6, namely 6 and 1, respectively, which is equal to 7 in this example.

\epsfbox{p4850.eps}
Figure 1. The optimal schedule of the example

Input 

Your program is to read from standard input. The input consists of T test cases. The number of test cases T is given on the first line of the input. The first line of each test case contains an integer n ( 1$ \le$n$ \le$500), the number of the given jobs. In the next n lines of each test case, the i-th line contains two integer numbers si and di, representing the serving time and the deadline of the job Ji, respectively, where 1$ \le$si$ \le$di$ \le$10, 000.

Output 

Your program is to write to standard output. Print exactly one line for each test case. The line contains the sum of the penalties of the jobs with the two largest penalties.

The following shows sample input and ouput for three test cases.

Sample Input 

3
6
1 7
4 7
2 4
2 15
3 5
3 8
7
2 17
2 11
3 4
3 20
1 20
4 7
5 14
10
2 5
2 9
5 10
3 11
3 4
4 21
1 7
2 9
2 11
2 23

Sample Output 

7
0
14

  每个任务需要s的时间完成,截止日期是d,超过d算超时,问所有任务超时最多的2个任务的超时和最小是多少。

  要是问1个任务超时最小就容易了,问题是2个任务的和,这个我真是没想出来,搜了解题报告,但是都只有方法没说为什么这么做,想了好久算是把这个原因搞清楚了=。=

  首先这个题可以暴力所有排序方法,找出答案,但是明显浪费时间。

  若先按截止时间排序,这样可以保证超时最长的是所有排序方法中超时最长的里最小的。在这个顺序中找出超时最多和第二多的两个位置。设这两个位置靠前的为p1,靠后的为p2。然后把[0,p1]中的每一个位置移动到p2之后(只移动一个,比如一开始是0...p1...p2,移动0就是 1...p1...p2,0  ,移动1就是 0,2...p1...p2,1 ,以此类推),在当前序列找到第一大和第二大,看是否能更新答案。

  这样其实也就相当于遍历了所有排序,只是省掉了一些不必要的顺序。

  1.p1之前的任意两个数交换位置或p2之后的任意两个数交换位置或p1,p2之间的任意两个数交换位置。这个是不必要的,这些顺序交换不能改变p1,p2位置的值,只有可能让答案更大。

  2.p1之前的和p1,p2之间的交换位置。这个也是不必要的。先按截止时间排序已经保证了最大值最小,在最大值最小的情况下第二大值最小...若p1位置是最大值,这么做只可能让最大值更大,不影响第二大值。若p1位置是第二大值,就可能让第二大值更大,不影响最大值。总之也是只可能让答案变大。

  3.p2之后的和p1,p2之间的交换位置。和2同理,不必要。

  4.[0,p1]和p2之后的交换位置。而且把[0,p1]中的一个移动到p2后一个是最优的,因为如果移到p2后的任意位置,就相当于先移动到p2后一个位置,再把它和p2后的任意位置交换,第二步在前面说了是不必要的。移动到p2后一个位置,这样做虽然会让移动的那个变成比原来的最大值更大,但同时原来的最大值会往前移动会变小,这样也许和比原来的和小。要注意的是现在的第二大的值不一定是原来的最大值的那个任务,也许它减小了一段时间后,原来的序列里没有减小时间的比它大。所以要在整个序列里找此时的最大、第二大值。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define INF 0x3f3f3f3f
#define MAXN 510
#define MAXM 60
#define eps 1e-9
#define pii pair<int,int>
using namespace std;
int T,N,t[MAXN];
struct duty{
    int s,d;
    bool operator < (const duty &x) const{
        return d<x.d;
    }
}a[MAXN];
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d",&N);
        for(int i=0;i<N;i++) scanf("%d%d",&a[i].s,&a[i].d);
        sort(a,a+N);
        t[0]=a[0].s;
        for(int i=1;i<N;i++) t[i]=t[i-1]+a[i].s;
        int first=0,second=0,p1=0,p2=0;
        for(int i=0;i<N;i++){
            if(t[i]-a[i].d>=first){
                second=first;
                p2=p1;
                first=t[i]-a[i].d;
                p1=i;
            }
            else if(t[i]-a[i].d>second){
                second=t[i]-a[i].d;
                p2=i;
            }
        }
        int P1=min(p1,p2),P2=max(p1,p2),s=t[P2],ans=first+second;
        for(int i=0;i<=P1;i++){
            first=0;
            second=0;
            int m;
            for(int j=0;j<N;j++){
                if(j<i) m=t[j];
                else if(j==i) m=s;
                else if(j<=P2)m=t[j]-a[i].s;
                else m=t[j];
                m=max(m-a[j].d,0);
                if(m>=first){
                    second=first;
                    first=m;
                }
                else if(m>second) second=m;
            }
            ans=min(ans,first+second);
        }
        printf("%d\n",ans);
    }
    return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值