[ZJOI2010]贪吃的老鼠 网络流

~~~题面~~~

题解:

这是一道强题emmmm,做法非常巧妙,,,我也是看了好久大佬题解才看明白一点

首先考虑没有限制的情况,即n个老鼠可以在同一时刻吃同一块奶酪

对各个时间段拆点,连奶酪 ---> 老鼠(反过来也是一样的,只不过不方便),相连的奶酪要符合时间段的限制,

相当于把老鼠拆成很多个小时刻,连向这个时刻它可以吃的奶酪,流量为它在这段时间内可以吃的奶酪总量,

限流可以在汇点到老鼠的路径上进行。

但这个并不能满足同一时刻一块奶酪只能被一个老鼠吃这个条件,因此我们对老鼠再拆点,

把每个老鼠拆成的小时刻再根据速度差分,

比如8 4 2 1,四只老鼠。差分后就是:

8 - 4 = 4;

4 - 2 = 2;

2 - 1 = 1;

1 - 1 = 1;

然后按照编号来定权值

流量就为编号 * 速度(差分后) * 时间;

为什么这样?

8 = 4 + 2 + 1 + 1

4 = 2 + 1 + 1

2 = 1 + 1

1 = 1

可以很明显看到这是一个三角形,且每层都是相同的数字,对应到我们差分数组,对于每个差分后的速度,刚好有编号个,

这样就可以保证总的流量合法了。

那为什么这样可以保证同一时刻只有一只老鼠呢?

可以这样感性的理解:

注意到任意一只老鼠都可以由差分数组凑出,那么不管网络流怎样跑出答案,我们都可以通过分解一下流量,加加减减之类的数学方法凑成这几只老鼠,因此是合法的。

也就是说网络流跑出的东西也许跟原图不一样,但它可以用来判断是否合法(满流即合法),这就够了。

因此我们二分答案,每次都重新建图,跑最大流,满流为true,else 为 false。

代码有点长(打的isap),改成dinic应该会短很多

(数组开这么小是卡常后的结果,,,,)

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 5000
  5 #define ac 20000
  6 #define eps 1e-6
  7 int x, n, m, T, cnt, tmp, ss, tt;
  8 int p[AC], v[AC], last[AC];
  9 double all, ll, rr, mid, ans, addflow;
 10 double r[AC], d[AC], haveflow[ac], t[AC];
 11 int Head[AC], Next[ac], date[ac], tot;
 12 int good[AC], have[AC], c[AC];
 13 int q[AC], tail, head;
 14 /*神奇的网络流,,,,
 15 对每个时间点进行离散化*/
 16 inline int read()
 17 {
 18     int x = 0;char c = getchar();
 19     while(c > ‘9‘ || c < ‘0‘) c = getchar();
 20     while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
 21     return x;
 22 }
 23
 24 inline void upmin(double &a, double b)
 25 {
 26     if(b < a) a = b;
 27 }
 28
 29 inline void add(int f, int w, double S)
 30 {
 31     date[++tot] = w, Next[tot] = Head[f], haveflow[tot] = S, Head[f] = tot;
 32     date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
 33     //printf("%d ---> %d %.4lf\n",f,w,S);
 34 }
 35
 36 bool bfs()
 37 {
 38     int x, now;
 39     head = tail = 0;
 40     c[tt] = 1, have[1] = 1, q[++tail] = tt;
 41     while(head < tail)
 42     {
 43         x = q[++head];
 44         for(R i = Head[x]; i ; i = Next[i])
 45         {
 46             now = date[i];
 47             if(haveflow[i ^ 1] && !c[now])
 48             {
 49                 c[now] = c[x] + 1;
 50                 q[++tail] = now;
 51                 ++have[c[now]];
 52             }
 53         }
 54     }
 55     memcpy(good, Head, sizeof(Head));
 56     return c[ss];
 57 }
 58
 59 void aru()
 60 {
 61     while(x != ss)
 62     {
 63         haveflow[last[x]] -= addflow;
 64         haveflow[last[x] ^ 1] += addflow;
 65         x = date[last[x] ^ 1];
 66     }
 67     ans += addflow;
 68 }
 69
 70 double isap()
 71 {
 72     int now; bool done;
 73     x = ss, addflow = INT_MAX;
 74     while(c[ss] != ac + 1)
 75     {
 76         if(x == tt) aru(), addflow = INT_MAX;
 77         done=false;
 78         for(R i = good[x]; i ; i = Next[i])
 79         {
 80             now = date[i];
 81             if(c[now] == c[x] - 1 && haveflow[i])
 82             {
 83                 last[now] = i;
 84                 upmin(addflow, haveflow[i]);
 85                 good[x] = i;
 86                 done = true;
 87                 x = now;
 88             }
 89         }
 90         if(!done)
 91         {
 92             int go = ac;
 93             for(R i = Head[x]; i ; i = Next[i])
 94             {
 95                 now = date[i];
 96                 if(haveflow[i] && c[now]) go = c[now];
 97             }
 98             if(!(--have[c[x]])) break;
 99             ++have[c[x] = go + 1];
100             good[x] = Head[x];
101             if(x != ss) x = date[last[x] ^ 1];//这是回到上一个节点啊
102         }
103     }
104     return ans;
105 }
106
107 inline bool cmp(double a, double b)
108 {
109     return a > b;
110 }
111
112 void pre()
113 {
114     tot = 1, all = 0;
115     n = read() ,m = read();
116     for(R i = 1; i <= n; i++)
117     {
118         p[i] = read(), r[i] = read(), d[i] = read();
119         all += (double)p[i];
120     }
121     for(R i = 1; i <= m; i++) v[i] = read();
122     sort(v + 1, v + m + 1, cmp);//error!!!老鼠是m只!!!!不是n只!!!
123     rr = (double) all / (double)v[1] + 1.0, ll = 0;
124     for(R i = 1; i < m; i++) v[i] -= v[i + 1];//对速度差分
125 }
126
127 void build()
128 {
129     tot = 1, ans = 0;
130     memset(Head, 0, sizeof(Head));//应该要放这里重置吧
131     memset(have, 0, sizeof(have));
132     memset(c, 0, sizeof(c));
133     for(R i = 1; i <= n; i++)
134     {
135         add(ss, i, p[i]);
136         t[2 * i - 1] = r[i], t[2 * i] = d[i] + mid;//为离散化做准备
137     }
138     sort(t + 1, t + 2 * n + 1);//准备离散化了
139     cnt =  0, tmp = n;//因为不能和前n个奶酪的编号重了
140     int a = 2 * n;
141     t[a + 1] = INT_MAX;//不然最后一个点进不来
142     for(R i = 1; i <= a; i++)
143         if(t[i + 1] - t[i] > eps) t[++cnt] = t[i];//去重
144     for(R i = 1; i <= m; i++)//枚举老鼠
145     {
146         for(R j = 2; j <= cnt; j++)//因为要两个时间点才组成一个时间段
147         {
148             ++tmp;
149             add(tmp, tt, i * v[i] * (t[j] - t[j - 1]));//连离散化的老鼠到汇点
150             for(R k = 1; k <= n; k++)//枚举奶酪
151             {
152                 if(r[k] - t[j-1] < eps && (d[k] + mid - t[j] > - eps))
153                     add(k, tmp, v[i] * (t[j] - t[j - 1]));//连奶酪向老鼠
154             }//r可以小于t(早就开始了),所以负数也合法,后面是同理的,只是移项了tarjan123
155
156         }
157     }
158 }
159
160 void half()
161 {
162     ss = 2 * m * n + n + 1, tt = ss + 1;//error!!!ss是要2 * m * n + n + 1啊
163     while(rr - ll > eps)
164     {
165         mid = (rr + ll) / 2.0;
166         build();
167         if(bfs() && all - isap() < eps) rr = mid;
168         else ll = mid;
169         //printf("%.4lf\n",ans);
170         //printf("%.4lf %.4lf\n\n",ll,rr);
171     }
172     printf("%lf\n", ll);
173 }
174
175 void work()
176 {
177     T=read();
178     while(T--)
179     {
180         pre();
181         half();
182     }
183
184 }
185
186 int main()
187 {
188 //    freopen("in.in","r",stdin);
189     //freopen("cheese.out","w",stdout);
190     work();
191 //    fclose(stdin);
192     //fclose(stdout);
193     return 0;
194 }

原文地址:https://www.cnblogs.com/ww3113306/p/9175976.html

时间: 2024-08-21 08:04:02

[ZJOI2010]贪吃的老鼠 网络流的相关文章

[ZJOI2010]贪吃的老鼠(网络流+建图)

题目描述 奶酪店里最近出现了m只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产n块奶酪,其中第i块的大小为pi,会在第ri秒被生产出来,并且必须在第di秒之前将它吃掉.第j只老鼠吃奶酪的速度为sj,因此如果它单独吃完第i快奶酪所需的时间为pi/sj.老鼠们吃奶酪的习惯很独特,具体来说: (1) 在任一时刻,一只老鼠最多可以吃一块奶酪: (2) 在任一时刻,一块奶酪最多被一只老鼠吃. 由于奶酪的保质期常常很短,为了将它们全部吃掉,老鼠们需要使用一种神奇的魔法来延长奶酪的保质期.

[ZJOI2010]贪吃的老鼠

P2570 [ZJOI2010]贪吃的老鼠 在Ta的博客查看 显然二分,最大流判定 要满足两个条件: (1) 在任一时刻,一只老鼠最多可以吃一块奶酪: (2) 在任一时刻,一块奶酪最多被一只老鼠吃. 先按照奶酪的边界进行离散化, 变成num个块 卡精度啊,,,,, inf设太大了 并且为了防止被inf卡,可以直接记录ret表示流出流量,直接返回ret即可 #include<bits/stdc++.h> #define reg register int #define il inline #de

Luogu P2570 [ZJOI2010]贪吃的老鼠

Luogu P2570 [ZJOI2010]贪吃的老鼠 题目描述 奶酪店里最近出现了\(m\)只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产\(n\)块奶酪,其中第\(i\)块的大小为\(pi\),会在第\(ri\)秒被生产出来,并且必须在第\(di\)秒之前将它吃掉.第j只老鼠吃奶酪的速度为\(sj\),因此如果它单独吃完第i快奶酪所需的时间为\(pi/sj\).老鼠们吃奶酪的习惯很独特,具体来说: (1) 在任一时刻,一只老鼠最多可以吃一块奶酪: (2) 在任一时刻,一

[zjoi2010]cheese

题目: 贪吃的老鼠(cheese.c/cpp/pas/in/out) 时限:每个测试点10秒 [问题描述] 奶酪店里最近出现了m只老鼠!它们的目标就是把生产出来的所有奶酪都吃掉.奶酪店中一天会生产n块奶酪,其中第i块的大小为pi,会在第ri秒被生产出来,并且必须在第di秒之前将它吃掉.第j只老鼠吃奶酪的速度为sj,因此如果它单独吃完第i快奶酪所需的时间为pi/sj.老鼠们吃奶酪的习惯很独特,具体来说: (1) 在任一时刻,一只老鼠最多可以吃一块奶酪: (2) 在任一时刻,一块奶酪最多被一只老鼠吃

网络流专题练习Day1

04/16 一共做了8道题 首先网络流目前自己掌握的只有最大流Dinic算法和普通的费用流算法 有空还要去学习一下SAP和ZKW费用流(flag早早立在前面以后看到都有动力... 但网络流的算法个人认为并不重要,解题的关键和思维的难点都在建图上 所以这一类的题目往往将模板一打,剩下的代码就非常短 将看起来毫无关系的题目转化成网络流做十分有趣 BZOJ3931 CQOI2015网络吞吐量 题目描述 路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点.网络中实

贪吃蛇“大作战”(二)

从对象出发看贪吃蛇 在上一篇博客中,我通过逐行分析代码对贪吃蛇的运行机制做了一个简要的介绍:逐行分析后可以看出这个贪吃蛇例程的编写范式更突出面向过程:该例程先是预设食物和贪吃蛇的位置参数,然后通过选择逻辑判断外部命令的值,根据不同的值做出不同的动作,最后在地图上将之前的动作"渲染",达到贪吃蛇"前进"的效果:如此下来,一步一步地根据"看得到的逻辑"实现了贪吃蛇的运行机制. 现在,我从面向对象的编程思维来对这个贪吃蛇运行机制做一个分析.从下面这幅图

安卓贪吃蛇项目包!!

我在博客上看见很多有关于安卓开发贪吃蛇的博文,但是都不知道他们所用的软件.版本是什么,所以在自己下载的软件上运行的时候总是出不来结果,作为一只安卓课程老师只上了一节课就让我们自己做课程设计的菜鸟来说,这是何其困哪的一件事,安卓什么也不懂,运行环境也是一点也不熟悉.我们老师要求我们用eclipse来运行,有没有人是用这个做过的啊?求帮助!!真的是不会了,找了很多的项目包运行的时候都会出错,永远不会出现贪吃蛇的界面,宝宝真的快疯了.还附上了我所用的软件,有没有好心人解答下这个问题. 本来打算私聊项目

hiho 第118周 网络流四&#183;最小路径覆盖

描述 国庆期间正是旅游和游玩的高峰期. 小Hi和小Ho的学习小组为了研究课题,决定趁此机会派出若干个调查团去沿途查看一下H市内各个景点的游客情况. H市一共有N个旅游景点(编号1..N),由M条单向游览路线连接.在一个景点游览完后,可以顺着游览线路前往下一个景点. 为了避免游客重复游览同一个景点,游览线路保证是没有环路的. 每一个调查团可以从任意一个景点出发,沿着计划好的游览线路依次调查,到达终点后再返回.每个景点只会有一个调查团经过,不会重复调查. 举个例子: 上图中一共派出了3个调查团: 1

bzoj1834: [ZJOI2010]network 网络扩容

努力看了很久样例一直过不了...然后各种输出中间过程啊巴拉巴拉弄了1h,没办法了...然后突然想到啊原来的边可以用啊为什么不用...于是A了...感人肺腑 #include<cstdio> #include<cstring> #include<queue> #include<iostream> #include<algorithm> using namespace std; #define rep(i,n) for(int i=1;i<=n