POJ

一开始是往二分上去想的,如果risk是x,题目要求则可以转化为一个不等式,Si + x >= sigma Wj ,j表示安排在i号牛上面的牛的编号。

如果考虑最下面的牛那么就可以写成 Si + x  ≥ sum W - Wi,为了方便处理把i号牛的信息合并到一起 → Si + Wi + x ≥sum W。

二分x的时候,x是个常量,而从下面往上去安排牛的时候,下面的牛是没有影响决策的,可以看成把Wi去掉。

于是得到一个贪心的选法,把牛按照Si+Wi排序,从下面往上安排牛,可选择的牛应该满足Si+Wi≥lim W, lim W = sum W - x - sigma Wj, j是在i下面的牛。

因为选择是会改变和号部分,lim W越小后面越容易选到,所以我用优先队列选择满足条件Wi最大的一个。复杂度是O(log(T)*nlogn+nlogn)。

交上去A了以后,看了看别人的runtime,感觉复杂度不对,实际上不用每次选取满足条件Wi最大的一个,因为limW是单减的,当Si+Wi可选的时候,后面的牛

一定可以选到,所以只要考虑从后往前数是否存在第一个Si+Wi不可选,如果存在,那么lim W其实和后面的选择顺序是没有关系的。

不用优先队列,复杂度变成了O(logT*n+nlogn)。

最终极的做法应该是贪心了,既然lim W = sum W - x - sigma Wj。sigma Wj的顺序不用考虑,那么只要在不满足条件的时候修改x就好了。

/*********************************************************
*            ------------------                          *
*   author AbyssalFish                                   *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAX_N = 5e4;
//int W[MAX_N], S[MAX_N];
typedef pair<int,int> pii;
#define WS first
#define W second
int N, sumW;
pii dat[MAX_N];

bool P(int x)
{
    int limW = sumW-x, j = N-1;
    while(j >= 0 && dat[j].WS >= limW){
        limW -= dat[j--].W;
    }
    return j < 0;
}

//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    scanf("%d",&N);
    int W, S, max_S = 0;
    for(int i = 0; i < N; i++){
        scanf("%d%d",&W,&S);
        dat[i].WS = W+S;
        max_S = max(max_S, S);
        sumW += dat[i].W = W;
    }
    sort(dat,dat+N);
    int lb = -max_S, ub = sumW;
    while(lb < ub){
        int md = (lb+ub)>>1;
        P(md) ? ub = md : lb = md+1;
    }
    printf("%d
",lb);
    return 0;
}
原文地址:https://www.cnblogs.com/jerryRey/p/4969653.html