HDU 6240 Server(2017 CCPC哈尔滨站 K题,01分数规划 + 树状数组优化DP)
题目链接 2017 CCPC Harbin Problem K
题意 给定若干物品,每个物品可以覆盖一个区间。现在要覆盖区间$[1, t]$。
求选出来的物品的$\frac{∑a_{i}}{∑b_{i}}$的最小值。
首先二分答案,那么每个物品的权值就变成了$x * b_{i} - a_{i}$
在判断的时候先把那些权值为正的物品全部选出来,
然后记录一下从$1$开始可以覆盖到的最右端点的位置。
接下来开始DP,按照区间的端点升序排序(左端点第一关键字,右端点第二关键字)
问题转化为能否用剩下的物品覆盖尚未覆盖的区间。
那么直接用树状数组优化DP就可以了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define fi first
#define se second
const int N = 1e5 + 10;
struct node{
int s, t;
double a, b;
bool flag;
void scan(){ scanf("%d%d%lf%lf", &s, &t, &a, &b); }
friend bool operator < (const node &a, const node &b){
return a.s == b.s ? a.t < b.t : a.s < b.s;
}
} a[N];
int T;
int n, m;
double c[N];
double l, r;
void update(int x, double val){
for (; x; x -= x & -x) c[x] = min(c[x], val);
}
double query(int x){
if (x == 0) return 0;
double ret = 1e9;
for (; x <= m; x += x & -x) ret = min(ret, c[x]);
return ret;
}
bool check(double x){
int mx = 0;
double ss = 0;
rep(i, 0, m) c[i] = 1e9;
rep(i, 1, n){
if (a[i].b * x - a[i].a > 0){
ss += a[i].b * x - a[i].a;
a[i].flag = true;
if (a[i].s - 1 <= mx){
mx = max(mx, a[i].t);
}
}
else a[i].flag = false;
}
if (mx == m) return true;
update(mx, 0);
rep(i, 1, n){
if (!a[i].flag){
double mi = query(a[i].s - 1);
update(a[i].t, mi + a[i].a - a[i].b * x);
}
else{
double mi = query(a[i].s - 1);
update(a[i].t, mi);
}
}
return ss >= query(m);
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
rep(i, 1, n) a[i].scan();
sort(a + 1, a + n + 1);
l = 0, r = 1e3 + 10;
rep(i, 1, 50){
double mid = (l + r) / 2.00;
if (check(mid)) r = mid;
else l = mid;
}
printf("%.3f\n", l);
}
return 0;
}