首页 > 代码库 > [zjoi2010]cheese
[zjoi2010]cheese
题目:
贪吃的老鼠(cheese.c/cpp/pas/in/out)
时限:每个测试点10秒
[问题描述]
奶酪店里最近出现了m只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉。奶酪店中一天会生产n块奶酪,其中第i块的大小为pi,会在第ri秒被生产出来,并且必须在第di秒之前将它吃掉。第j只老鼠吃奶酪的速度为sj,因此如果它单独吃完第i快奶酪所需的时间为pi/sj。老鼠们吃奶酪的习惯很独特,具体来说:
(1) 在任一时刻,一只老鼠最多可以吃一块奶酪;
(2) 在任一时刻,一块奶酪最多被一只老鼠吃。
由于奶酪的保质期常常很短,为了将它们全部吃掉,老鼠们需要使用一种神奇的魔法来延长奶酪的保质期。将奶酪的保质期延长T秒是指所有的奶酪的di变成di+T。同时,使用魔法的代价很高,因此老鼠们希望找到最小的T使得可以吃掉所有的奶酪。
[输入数据]
输入文件的第一行包含一个整数K,表示输入文件中数据的组数。
每组数据的第一行包含两个整数n和m,分别表示奶酪和老鼠的数量。接下来的n行每行包含三个整数pi,ri,di。最后m行每行包含一个整数,表示sj。pi,ri,di,sj的含义如上文所述。
[输出数据]
输出文件中包含K行,每行包含一个实数,表示你找到的最小的T。你的答案和标准答案的绝对误差不应超过。
输入样例
2
2 2
13 0 4
10 1 3
4
2
1 1
1 0 2
1
输出样例
0.5
0
样例说明
第一组数据中:
l 第0到第1秒:第一只老鼠吃第一块奶酪;
l 第1到第3.5秒:
- 第一只老鼠吃第二块奶酪;
- 第二只老鼠吃第一块奶酪;
l 第3.5到第4.5秒:第一只老鼠吃第一块奶酪。
[数据范围]
思路:
很好的一道题。。可惜我太弱了。。只想到要二分+网络流判定。。然后就什么也不会了。。只能看了题解。。
本题比较难的是一个时间点只能有一只老鼠,而一个老鼠一个时间点只能吃一个。。
朴素的建图是:
让S向每个奶酪连边,流量为pi
把老鼠按照时间段拆点,然后对于每个点, 向T连边,流量为该时间段能吃掉的奶酪数量
并且把奶酪向每个时间段的老鼠连边,流量为流量为该时间段能吃掉的奶酪数量
最后判断是否满流。。
但是,这样会出现有一个时间点有两只老鼠吃完的情况。。。怎么办?
题解就很巧了。。
我们对老鼠吃的速度也进行分段, 每段为对于前一只老鼠的速度差。。
试想一下,如果一个奶酪一个时间段(长度为T)内有若干只老鼠吃了,不妨设为为3只老鼠,a1,a2,a3,并且速度为s1,s2,s3, 同样假设s1< s2 < s3,
那么是不是这一段时间内老鼠吃了T1*s1 + T2(s2-s1) + T3*(s3-s2)
那是不是就可以把老鼠某个时间段吃的方案等价于多少个速度差吃的方案~~
so,构图如下:
让S向每个奶酪连边,流量为pi
对于每个速度段(第j段为sj)进行拆点为vij‘按照时间段(假设时间长为t)拆点,然后对于每个点, 向T连边,流量为该时间段该速度段sj在t时间内的产(sj*t*j)
并且把奶酪向每个时间段的速度段vij连边,流量为sj*t
最后判断是否满流。。
这样一来,每个速度段在每个时间段最多只有出现一个,这样就满足一对一的情况。。
好吧。。说的很混乱。。直接上代码吧。。
code:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstdlib> 6 #include <cstring> 7 #include <vector> 8 #define maxn 1200 9 #define Inf 0x3fffffff 10 #define maxm 210000 11 #define eps 1e-8 12 #define M0(a) memset(a, 0, sizeof(a)) 13 using namespace std; 14 int cmp(double x, double y){ 15 return fabs(x - y) <= eps; 16 } 17 int sgn(double x){ 18 return (x > eps) - (x < -eps); 19 } 20 struct oo{ 21 int y, next; 22 double f; 23 }; 24 struct MaxFlow{ 25 int n, S, T, tot; 26 int son[maxn], dist[maxn], gap[maxn]; 27 oo e[maxm]; 28 double sap(int x, double aug){ 29 if (x == T) return aug; 30 int mind = n; 31 double sum = 0, f; 32 for (int p = son[x]; p != -1; p = e[p].next){ 33 int y = e[p].y; 34 if (dist[y] + 1 == dist[x] && e[p].f){ 35 f = sap(y, min(e[p].f, aug - sum)); 36 e[p].f -= f; 37 e[p^1].f += f; 38 sum += f; 39 if (sum == aug || dist[S] >= n) return sum; 40 } 41 if (e[p].f) mind = min(mind, dist[y]); 42 } 43 if (!sum){ 44 if (!(--gap[dist[x]])) dist[S] = n; 45 ++gap[dist[x] = mind + 1]; 46 } 47 return sum; 48 } 49 50 void add(int x, int y, double f){ 51 e[tot].y = y; e[tot].f = f; 52 e[tot].next = son[x]; son[x] = tot++; 53 e[tot].y = x; e[tot].f = 0; 54 e[tot].next = son[y]; son[y] = tot++; 55 } 56 57 void init(int S, int T, int n){ 58 memset(son, -1, sizeof(son)); 59 tot = 0; 60 this->S = S, this->T = T, this->n = n; 61 } 62 double maxflow(){ 63 M0(gap); 64 M0(dist); 65 gap[0] = n; 66 double ans = 0; 67 while (dist[S] < n) ans += sap(S, Inf); 68 return ans; 69 } 70 } F; 71 int n, m, p[maxn], r[maxn], d[maxn], s[maxn]; 72 73 void init(){ 74 scanf("%d%d", &n, &m); 75 for (int i = 1; i <= n; ++i) 76 scanf("%d%d%d", &p[i], &r[i], &d[i]); 77 for (int i = 1; i <= m; ++i) 78 scanf("%d", &s[i]); 79 sort(s+1, s+1+m, greater<int>()); 80 for (int i = 1; i < m; ++i) 81 s[i] -= s[i+1]; 82 } 83 84 int check(double add){ 85 vector<double> v; 86 for (int i = 1; i <= n; ++i) 87 v.push_back(r[i]), v.push_back(d[i]+add); 88 sort(v.begin(), v.end()); 89 v.erase(unique(v.begin(), v.end(), cmp), v.end()); 90 int S = 0, T = m*(v.size()-1)+n+1; 91 F.init(0, T, T+1); 92 for (int i = 1; i <= n; ++i) 93 F.add(S, i, p[i]); 94 for (int i = 0; i + 1 < (int)v.size(); ++i){ 95 double dur = v[i+1]-v[i]; 96 for (int j = 1; j <= m; ++j) 97 F.add(i * m + n + j, T, j*s[j]*dur); 98 for (int j = 1; j <= n; ++j) 99 if (sgn(r[j]-v[i]) <= 0 && sgn(d[j]+add-v[i+1]) >= 0)100 for (int k = 1; k <= m; ++k)101 F.add(j, n+i*m+k, dur*s[k]); 102 }103 double sum = 0;104 for (int i = 1; i <= n; ++i)105 sum += p[i];106 double flow = F.maxflow();107 return sgn(flow - sum) == 0; 108 }109 110 void solve(){111 double l = 0, r = 3000000;112 for (int i = 0; i < 40; ++i){113 double mid = (l + r) / 2.0;114 check(mid) ? r = mid : l = mid;115 }116 printf("%.7lf\n", l); 117 }118 119 int main(){120 freopen("a.in", "r", stdin);121 freopen("a.out", "w", stdout);122 int T;123 scanf("%d", &T);124 while (T--){125 init();126 solve(); 127 }128 }