hdu 6240

来源:互联网 发布:php forreach 编辑:程序博客网 时间:2024/06/01 09:04

Server

Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 623 Accepted Submission(s): 94

Problem Description
Alice and Bob are working on a new assignment. In this project, they need to access some information on a website and monitor this web site for consistent t days. In others words, in each day, there must be at least one server in work. Luckily, they can rent some servers in the lab. According to the schedule, there are totally N servers being available. The i-th server can be used from the Si-th day to the Ti-th day. However, using the i-th server require Ai dollars. After a long time of persuasion, the administrator of the machine room agree to give Alice and Bob discount. Each server is assigned with a discount number Bi. If the set of servers they want to use is S, they need to pay ∑i∈SAi∑i∈SBi dollars. As Alice and Bob are preparing the programs on the servers, they want your help to find a way to minimize the cost of servers.

Input
The first line is the number of test cases. For each test case, the first contains two positive integers N and t (N≤100000,t≤100000) as described above. In the next N lines, the i-th line consists of four integer Si, Ti, Ai, and Bi (1≤Si≤Ti≤t, 0 < Ai,Bi≤1000).

Output
For each test case, output an float-point number with three decimal places donating the minimum cost Alice and Bob need to pay. It is guaranteed that there is at least one feasible way to rent servers.

Sample Input
1
4 5
1 3 28 1
4 5 22 1
3 5 34 1
1 2 26 1

Sample Output
25.000
题意:给出n个服务器,每个服务器的工作时间是s到t,花费是a,b;选出一些服务器的集合s,求选出一个集合使得集合中的服务器(所有a的和)/(所有b的和) 最小。
做法:01分数规划,之前做过一个分数规划,现在感觉当时不是很理解。我现在对分数规划的理解是:对于给出的数据,选中和不选中有正的影响(是答案变大)或者有负的影响(使答案变小),而一般情况下我们做的dp题他们的影响都是一样的,会使结果变差,然后可以用dp找出所有结果中最好的那个,而对于这种题dp就不行了,因为有些边产生的影响不一样,所以不能直接dp,然后就可以通过二分解决了,二分答案,这样就可以把边分成两类,1:产生正影响,2:产生负影响。1的边选中,然后用dp从第二种边中找出产生负影响最小的方案,然后比较产生正影响和负影响的和。正影响和负影响的计算方法是加上mid*pt[i].b - pt[i].a;所有正影响大于负影响,那么说明可行,否则就说明不行。
优化:最开始我用线段树维护区间最小值,但是跑了9s多,差点超时,然后学习到了一个新的快一点维护区间最小值的方法,用数状数组去维护,跑了3s不到。

#include<bits/stdc++.h>using namespace std;const int N  = 1e5+100;int T,n,t;double sum[N<<2];struct node{    int l,r,a,b;    bool operator<(const node &p)const{        return l < p.l;    }    bool judge(double x){        return a*1.0/b < x;    }}pt[N];/*void build(int l,int r,int rt){    if(l == r) {        sum[rt] = 1e9;        return ;    }    int mid = l+r>>1;    build(l,mid,rt<<1);    build(mid+1,r,rt<<1|1);    sum[rt] = min(sum[rt<<1],sum[rt<<1|1]);}void update(int x,double c,int l,int r,int rt){    if(l == r){        sum[rt] = min(sum[rt],c);        return ;    }    int mid = l+r>>1;    if(mid >= x) update(x,c,l,mid,rt<<1);    else update(x,c,mid+1,r,rt<<1|1);    sum[rt] = min(sum[rt<<1],sum[rt<<1|1]);}double query(int L,int R,int l,int r,int rt){    if(L <= 0) return 0;    if(L <= l && R >= r){        return sum[rt];    }    int mid = l+r>>1;    double ret = 1e9;    if(mid >= L) ret = query(L,R,l,mid,rt<<1);    if(mid < R) ret = min(ret,query(L,R,mid+1,r,rt<<1|1));    return ret;}*/int lowbit(int x){    return x&(-x);}void update(int x,double c){    while(x > 0){        sum[x] = min(sum[x],c);        x -= lowbit(x);    }}double query(int x){    if(x == 0) return 0;    double ret = 1e9;    while(x <= t){        ret = min(ret,sum[x]);        x += lowbit(x);    }    return ret;}bool check(double x){    double ret = 0;    for(int i = 1;i <= t;i ++){        sum[i]= 1e9;    }    for(int i = 1;i <= n;i ++){        if(pt[i].judge(x)) ret += x*pt[i].b - pt[i].a;    }    for(int i = 1;i <= n;i ++){        double now = query(pt[i].l-1);        //cout << x << ' '<< i << ' '<<now << ' ' << pt[i].l << ' ' <<pt[i].r << endl;        if(!pt[i].judge(x)) now +=  pt[i].a - x*pt[i].b;        update(pt[i].r,now);    }    //cout << x << ' '<< sum << ' ' << query(t,t,1,t,1)<<endl;    return ret >= query(t);}int main(){    scanf("%d",&T);    while(T--){        scanf("%d %d",&n,&t);        for(int i = 1;i <= n;i ++){            scanf("%d %d %d %d",&pt[i].l,&pt[i].r,&pt[i].a,&pt[i].b);        }        sort(pt+1,pt+n+1);        double l = 0,r = 1001;        for(int i = 1;i <= 25;i ++){            double mid= (l+r)/2.0;            //cout << "!!!" << l << ' '<< r << ' '<< mid << endl;            if(check(mid)) r = mid;            else l = mid;        }        printf("%.3f\n",r);    }    return 0;}
原创粉丝点击