题目链接:点我点我:-) —只有洛谷有此题
题目描述:
奶酪店里最近出现了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。你的答案和标准答案的绝对误差不应超过10−4。
数据规模:
30%的数据中,1≤n,m≤5;
100%的数据中,1≤K≤5,1≤n,m≤30,1≤pi≤105, 0 ≤ri < di≤107,1≤sj≤105。
思路:
网络流好题
应该容易想到二分答案和离散化奶酪的时间点
先考虑朴素的建图,也就是不考虑每个时刻可以有多只老鼠在不同的小时刻吃同一个奶酪
1.我们将源点每个奶酪链流量为p[i]的边
2.然后把每只老鼠的每个时间段拆为一个点,再将其与对应的奶酪连边,流量为这只老鼠可以在这个时间段吃多少奶酪
3.所有的老鼠向汇点连边,无限流量
然而题目有限制,每个时刻可以有多只老鼠在不同的小时刻吃同一个奶酪,做法很巧妙,我们可以对老鼠的速度再进行拆点,把s[]从大到小排序然后与下一个作差,如”9 6 2 1” -> “3 4 1 1”,然后再将其与汇点连边的流量改为(rat为老鼠编号,dur为时间段大小,s为作差后的数组)rat*s[rat]*dur即可
为什么呢?
可以发现速度进行过如此处理之后,
9 = 3+4+1+1
6 = 4+1+1
2 = 1+1
1 = 1
9+6+2+1 = 3*1 + 4*2 + 1*3 + 1*4
(下面所有的s数组均为作差后的数组)
1.所以,当我们把老鼠点到汇点的流量限制设为rat*s[rat]*dur后,对于每一只老鼠,它‘吃’的量不超过实际可以‘吃’的量。(上述式子解释了,实际包括编号比它小的老鼠的吃的量)
2.每个老鼠点与奶酪的连边为s[rat]*dur,这样可以保证每只老鼠实际不可以超过自己这段时间可以吃的奶酪。
3.(对于第2点的解释)实际这段时间奶酪被吃的量不超过最快的老鼠的速度在这个时间段可以吃的量,如果每只老鼠在2点所述的边满流,那么实际相当于吃的最快的老鼠吃了这一整个时间段。而任何其它的情况均可以用别的老鼠凑出来,且由于第一点不会不合法
最后,每次二分答案,重新构图跑最大流,检查是否满流即可。
感想:
很好的网络流题目,然而连网络流都没想到是什么鬼???
原来网络流构图还可以这么神奇,把每个点使劲拆成这个鬼样子!!!
网络流的代码应该没什么问题了,但是构图能力还要再提高啦!
吐槽一下我调了3个小时的错误:
1.精度eps,写错居然可以导致最大流超过了我的p数组值的和!!!
2.我的天哪:(f=dfs(e.to, fmin(Mint, e.cap-e.flow)))>eps 这句话我不小心把 > eps写到最后一个括号里面了。。
代码:
//miaomiao 2017.2.3
#include
#include
#include
#include
#include
#include
using namespace std;
#define pb push_back
#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); i++)
#define MN (30+5)
#define N (3000+5)
#define eps (1e-6)
struct Edge{
int from, to;
double cap, flow;
};
struct Dinic{
vector<int> G[N];
vector edges;
int m, s, t;
int cur[N], d[N];
void init(){
For(i, 0, N-1) G[i].clear();
edges.clear();
}
void AddEdge(int u, int v, double cap){
edges.pb((Edge){u, v, cap, 0});
edges.pb((Edge){v, u, 0, 0});
m = edges.size();
G[u].pb(m-2); G[v].pb(m-1);
}
bool Bfs(){
queue<int> q;
q.push(s); Set(d, 0);
d[s] = 1;
int now;
while(!q.empty()){
now = q.front(); q.pop();
For(i, 0, G[now].size()-1){
Edge &e = edges[G[now][i]];
if(!d[e.to] && e.cap-e.flow > eps){
d[e.to] = d[now]+1;
q.push(e.to);
}
}
}
return d[t];
}
double dfs(int now, double Mint){
if(Mint < eps || now == t) return Mint;
double f;
double ret = 0.0;
for(int &i = cur[now]; i < G[now].size(); i++){
Edge &e = edges[G[now][i]];
if(d[e.to]==d[now]+1 && (f=dfs(e.to, fmin(Mint, e.cap-e.flow)))>eps){
e.flow += f; Mint -= f;
edges[G[now][i]^1].flow -= f;
ret += f;
if(Mint < eps) return ret;
}
}
return ret;
}
double Maxflow(){
double ret = 0.0;
while(Bfs()){
Set(cur, 0);
ret += dfs(s, (1LL<<60)*1.0);
}
return ret;
}
}Din;
int n, m;
double L, R, rp[MN], rr[MN], rd[MN], rs[MN], Ti[MN*2];
void solve(){
double mid, la, sum = 0;
int pn;
For(i, 1, n) sum += rp[i];
while(R-L > eps){
mid = (L+R)/2.0;
Din.init();
For(i, 1, n) Din.AddEdge(0, i, rp[i]);
For(i, 1, n){
Ti[2*i-1] = rr[i]; Ti[2*i] = rd[i]+mid;
}
sort(Ti+1, Ti+2*n+1);
Din.s = 0; Din.t = m*2*n+n+1; pn = n;
For(rat, 1, m){
la = Ti[1];
For(i, 2, 2*n){
if(Ti[i]-la < eps) continue;
++pn; Din.AddEdge(pn, Din.t, rat*rs[rat]*(Ti[i]-la));
For(j, 1, n)
if(rr[j]-la < eps && (rd[j]+mid)-Ti[i] > -eps)
Din.AddEdge(j, pn, rs[rat]*(Ti[i]-la));
la = Ti[i];
}
}
if(sum-Din.Maxflow() < eps) R = mid;
else L = mid;
}
printf("%lf\n", L);
}
int main(){
int T;
double tot;
scanf("%d", &T);
while(T--){
tot = 0.0;
scanf("%d%d", &n, &m);
For(i, 1, n) scanf("%lf%lf%lf", &rp[i], &rr[i], &rd[i]), tot += rp[i];
For(i, 1, m) scanf("%lf", &rs[i]); sort(rs+1, rs+m+1, greater<int>());
R = tot/rs[1] + 1.0; L = 0;
For(i, 1, m-1) rs[i] -= rs[i+1];
solve();
}
return 0;
}