• [SCOI2014]方伯伯运椰子


    嘟嘟嘟

    01分数规划思维题。

    题中要求交通总量不减少,那么如果总量增加的话,总费用就会增加,所以一定不是更优的解。那么总量守恒。

    这是不是就想到了网络流?对于每一个节点流入量等于流出量。然后就是很有思维的一个转化了:把压缩看成退流,把扩容看成增广。

    边(x, y)一次压缩,就建一条y -> x,容量为a - d的边。

    边(x, y)一次增广,就建一条x -> y,容量为b + d的边。也就是一次调整多出来的费用。那么这样建完图后,图中的一个环就代表一种调整方案!

    回头看题,让求某一个比值最小,那一定会想到01分数规划,令ans = max((X - Y) / k),那么ans >= (X - Y) / k,于是有ans * k + Y - X >= 0。按上述的建图,Y - X就是环上的边权和,k就是环中的点数(边数),所以这个式子相当于每经过一条边,这条边的边权就加一个ans,那么ans * k + Y - X >= 0就可以写成Σ(ans + ei) >= 0。二分的时候,如果存在负环,说明mid取小了,向右二分;否则向左二分。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<cstdlib>
     7 #include<cctype>
     8 #include<vector>
     9 #include<stack>
    10 #include<queue>
    11 using namespace std;
    12 #define enter puts("") 
    13 #define space putchar(' ')
    14 #define Mem(a, x) memset(a, x, sizeof(a))
    15 #define rg register
    16 typedef long long ll;
    17 typedef double db;
    18 const int INF = 0x3f3f3f3f;
    19 const db eps = 1e-8;
    20 const int maxn = 5e3 + 5;
    21 const int maxe = 3e3 + 5;
    22 inline ll read()
    23 {
    24   ll ans = 0;
    25   char ch = getchar(), last = ' ';
    26   while(!isdigit(ch)) {last = ch; ch = getchar();}
    27   while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
    28   if(last == '-') ans = -ans;
    29   return ans;
    30 }
    31 inline void write(ll x)
    32 {
    33   if(x < 0) x = -x, putchar('-');
    34   if(x >= 10) write(x / 10);
    35   putchar(x % 10 + '0');
    36 }
    37 
    38 int n, m;
    39 struct Edge
    40 {
    41   int nxt, to, w;
    42 }e[maxe << 1];
    43 int head[maxn], ecnt = -1;
    44 void addEdge(int x, int y, int w)
    45 {
    46   e[++ecnt] = (Edge){head[x], y, w};
    47   head[x] = ecnt;
    48 }
    49 
    50 db dis[maxn];
    51 bool vis[maxn], mak[maxn];
    52 bool spfa(int now, db x)
    53 {
    54   vis[now] = mak[now] = 1;
    55   for(int i = head[now]; i != -1; i = e[i].nxt)
    56     {
    57       if(dis[e[i].to] > dis[now] + e[i].w + x)
    58     {
    59       dis[e[i].to] = dis[now] + e[i].w + x;
    60       if(vis[e[i].to]) return 1;
    61       if(spfa(e[i].to, x)) return 1;
    62     }
    63     }
    64   vis[now] = 0;
    65   return 0;
    66 }
    67 
    68 bool judge(db x)
    69 {
    70   Mem(vis, 0); Mem(mak, 0);
    71   for(int i = 1; i <= n + 2; ++i)
    72     if(!mak[i])
    73       {
    74     if(spfa(i, x)) return 1;
    75       }
    76   return 0;
    77 }
    78 
    79 int main()
    80 {
    81   Mem(head, -1);
    82   n = read(); m = read();
    83   for(int i = 1; i <= m; ++i)
    84     {
    85       int x = read(), y = read(), a = read(), b = read(), c = read(), d = read();
    86       if(c) addEdge(y, x, a - d);
    87       addEdge(x, y, b + d);
    88     }
    89   db L = 0, R = 1e5;
    90   while(R - L > eps)
    91     {
    92       db mid = (L + R) / 2.00;
    93       if(judge(mid)) L = mid;
    94       else R = mid;
    95     }
    96   printf("%.2lf
    ", L);
    97   return 0;
    98 }
    View Code
  • 相关阅读:
    2-SAT模板
    AC自动机
    省选预备营-Day3(图论) 总结
    省选预备营-Day2(分治) 总结
    左偏树(可并堆)总结
    省选预备营-Day1(数据结构) 总结
    OI基础知识
    C++ 堆
    CH4601 普通平衡树
    java 函数形参传值和传引用的区别
  • 原文地址:https://www.cnblogs.com/mrclr/p/9856943.html
Copyright © 2020-2023  润新知