UVA1467 Installation (含完整证明)

摘要

贪心、证明

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4213

hooray!

恭喜我 A A 掉了这道题,用时 4h 4 h

先来证明一个定理

定理 p p :按照di进行排序所得到的方案,一定使得最大罚时最小。
证明:
对于任意两个元素,设 d1<d2 d 1 < d 2
1 1 在前时,罚时为t1=max(d1s1,d2s1s2),当然最后要和 0 0 max,但这不影响我们的证明
2 2 在前时,罚时为t2=max(d1s1s2,d2s1)=d2s1>t1
上述只是说了两个元素相邻的情况,如果不相邻的话也是一样,同样的方法即可证明
既然对于任意的两组, d d 比较小的都应该在前面,那么对于整个序列,就应该是递增的,这样以来对于任意的一对元素,都使得罚时最小,所以整个序列的最大罚时最小。

然后看这道题

先按照di为第一关键字, si s i 为第二关键字排序(可以自己举几个例子看看为什么要以 si s i 为第二关键字排序),先扫描一遍整个序列,记下最大罚时的位置和最小罚时的位置,序号较小的为 p1 p 1 ,序号较大的为 p2 p 2 ,这样序列被分成了三个区间 [1,p1) [ 1 , p 1 ) (p1,p2) ( p 1 , p 2 ) (p2,n] ( p 2 , n ] ,为了方便,我叫他们 A,B,C A , B , C
根据定理 p p (上述所证定理),A,B,C三个区间内最大罚时已经是最小了,如果我随便更改一个区间内部的顺序,得到新序列的最大罚时只会更大,如果这个数值大于了我所记录的 p1,p2 p 1 , p 2 处的罚时,就出现了一种新的解,而这组解比原先的情况更劣,所以说,在 A,B,C A , B , C 三个区间内部打乱顺序无助于寻找最优解。
我把 [0,p2),(p1,n] [ 0 , p 2 ) , ( p 1 , n ] 分别记为 D,E D , E ,如果这两个区间的顺序被扰乱,同样只可能会构造出更劣的解,不可能构造出更优的解。
那么现在只有一种方法可以构造出更优的解,就是改变 AC A ∪ C 中元素的顺序,且一定不能只是扰乱一个区间的顺序。
考虑如何扰乱,我当然可以暴力枚举所有的排列顺序,但是显然不现实。
我们不是有定理 p p 吗?
那么可以想到,只有那些满足扰乱后A,C里的元素的 di d i 都单调增的排列才有用。
那么就可以枚举每个元素在哪个区间里,这样是指数级别的,也不行
看看还有没有别的线索,如果我把一个 di d i 比较大的从 C C 扔到A里去,那么对于这个元素来讲,它的罚时变小了,而对于 p1,p2 p 1 , p 2 来讲,罚时都变大了,这种解是更劣的,那如果我把好多 C C 里的元素扔到A里去,显然答案会变得更劣更劣,因此 C C 中的元素没必要放到A里去。
那么 A A 中的元素可以放到C里吗?如果放一个过去的话,显然它本身的罚时变大了,而且肯定比原先的最大值还大,而原先的最大值变小了,甚至可能比它前面的某些元素还要小,因此最大值和次大值的和可能变小,可以更新一下 ans a n s
考虑我往下扔更多的元素,在 p2 p 2 这个位置上,是我扔下来的 di d i 最小的元素(因为已经排序了),由于这个位置的 si s i 的前缀和没有改变,而 di d i 却变小了,所以新得到的这个位置的罚时比原来的最大罚时还大(也有可能不变,但不会变小),原先的最大值,即使现在是次大值,整个的解也不会变得更优,更何况原来的最大值现在可能会被更大的数值取代,因此不会出现更优解。
经过以上论述,排序后唯一能够构造出更优解的方法,就是从 [1,p1] [ 1 , p 1 ] (端点可以单独验证一下)中选出一个元素,放到 p2 p 2 后面。
[1,p1] [ 1 , p 1 ] 中的每个元素都尝试一下,最终一定能够得到最优解。

代码

//Ì°ÐÄ
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#define cl(x) memset(x,0,sizeof(x))
#define maxn 550
#define inf 0x3f3f3f3f
using namespace std;
struct task
{
    int s, d, c;
}tsk[maxn], list1[maxn], list2[maxn];
int n, p1, p2, max1, max2, ans;
int read(int x=0)
{
    char c, f=1;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
    return f*x;
}
bool cmp(task &a, task &b){return a.d==b.d?a.s<b.s:a.d<b.d;}
int calc(int &p1, int &p2)
{
    int cost, i;
    max1=max2=0;
    for(i=1;i<=n;i++)
    {
        tsk[i].c=tsk[i-1].c+tsk[i].s;
        cost=max(0,tsk[i].c-tsk[i].d);
        if(cost>=max1)p2=p1, max2=max1, p1=i, max1=cost;
        else if(cost>max2)p2=i, max2=cost;
    }
}
void input()
{
    int i, cost, j, k;
    n=read();
    for(i=1;i<=n;i++)tsk[i].s=read(), tsk[i].d=read();
    sort(tsk+1,tsk+n+1,cmp);    
}
void solve()
{
    int i, p1, p2, x, y, j, c, size1=0, size2=0, tot;
    task tmp;
    calc(p1, p2);
    if(p1>p2)swap(p1,p2);
    ans=max1+max2;
    for(i=1;i<=p1;i++)
    {
        tmp=tsk[i];
        for(j=i;j<p2;j++)tsk[j]=tsk[j+1];
        tsk[p2]=tmp;
        calc(x,y);
        ans=min(ans,max1+max2);
        for(j=p2;j>i;j--)tsk[j]=tsk[j-1];
        tsk[i]=tmp;
    }
}
int main()
{
    int T;
    for(T=read();T--;)
    {
        ans=inf;
        input();
        solve();
        printf("%d\n",ans);
    }
    return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值