zoukankan      html  css  js  c++  java
  • [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
    ",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
    ",ans);
    170         //printf("%.4lf %.4lf
    
    ",ll,rr);
    171     }
    172     printf("%lf
    ", 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 }
  • 相关阅读:
    python学习(二十三) String(下) 分片和索引
    python学习(二十二) String(上)
    微服务网关
    【转】linux 软连接 硬链接
    设计模式--观察者模式
    设计模式--策略模式
    ubuntu-server 安装redis
    【转】linux的hostname修改详解
    【转】ftp的两种模式
    【转】linux下find查找命令用法
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9175976.html
Copyright © 2011-2022 走看看