首页 > 代码库 > UVA1467 - Installations

UVA1467 - Installations

题目链接


题意:有n个服务,每个服务都有安装时间s,截止时间d。如果任务没有在截止时间之前完成,会有惩罚值,假设完成时间为C,则惩罚值为max(C-d,0)。求最两个最大惩罚值之和的最小值。

思路:我们先按照截止时间d从小到大排序,如果d相同,则s小的排前面。这样处理得到的总的惩罚值是较优解,但不是最优解。排序之后,找到序列中惩罚值最大值和第二大值的两者中比较靠后的位置p,然后从0到p-1中枚举一个d放在p位置后面,d+1到p集体往左移动一位,这样就可以使得最大值和第二大值减小,但并不能保证每次在两个值都减小的过程中,依然保证两个还是最大值和第二大值,有可能牺牲的那个d会取代其中一个,所以要不断更新最小值。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 505;

struct task{
    int s, d;
}t[MAXN];

int n, p;

int cmp(task a, task b) {
    if (a.d != b.d)
        return a.d < b.d;
    else
        return a.s < b.s;
}

int deal(int x) {
    int cnt = 0, a = 0, b = 0, temp;
    for (int i = 0; i <= p; i++) {
        if (x == i)
            continue;
        cnt += t[i].s; 
        temp = max(cnt - t[i].d, 0);
        if (temp > a) {
            b = a;
            a = temp; 
        }
        else if (temp > b) 
            b = temp;  
    }
    cnt += t[x].s; 
    temp = max(cnt - t[x].d, 0);
    if (temp > a) {
        b = a;
        a = temp; 
    }
    else if (temp > b) {
        b = temp; 
    } 
    return a + b;
}

int main() {
    int cas;
    scanf("%d", &cas);
    while (cas--) {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) 
            scanf("%d%d", &t[i].s, &t[i].d);
        sort(t, t + n, cmp); 

        int cnt = 0, a = 0, b = 0; 
        for (int i = 0; i < n; i++) {
            cnt += t[i].s; 
            int temp = max(cnt - t[i].d, 0);
            if (temp > a) {
                b = a;
                a = temp; 
                p = i;
            }
            else if (temp > b) {
                b = temp; 
                p = i;
            } 
        }

        int ans = a + b;;
        for (int i = 0; i < p; i++)
            ans = min(ans, deal(i));
        printf("%d\n", ans);
    }
    return 0;
}