zoukankan      html  css  js  c++  java
  • POJ 3308, ZOJ 2874

    题意:

    R×C的网格中有N个点,选取其中的若干行和若干列将这些点全部覆盖(选取一行/列可以覆盖该行/列的所有点,要求每个点至少被覆盖一次)。

    选取第i行的代价为r[i],选取第i列的代价为c[i],求代价乘积的最小值。

    R, C, N≤50,r[i], c[i]均为≥1.0的正实数

    转化成最小割问题。建图方法如下:

    (1)设立源点S和汇点T,对于每行、每列分别建立一个对应的节点;

    (2)从源点出发,向每行连一条边,权值为r[i]的对数(底数自拟)。转化成对数可以将乘积最优问题转化为和最优。

    (3)从每列出发,分别向汇点连一条边,权值为c[i]的对数;

    (4)对于网格中的每一个点,设它在第x行第y列,在图中从第x行向第y列连一条边,权值为+∞。

    注意:由于诡异的精度误差,inf的值取太大可能会导致WA!

    代码如下:(前50行是个比较通用的大型边表模板,在赛场上如果有多道图论题的话可以省点时间……吧(这玩意儿会有卵用么))

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <queue>
      5 #include <cmath>
      6 
      7 #define FILL(a, c) memset(a, c, sizeof(a))
      8 
      9 template <int V, int E> //V为最大节点数,E为最大边数
     10 struct EList
     11 {
     12     struct Edge { int to; double flow; };
     13     struct Node: Edge
     14     {
     15         int next;
     16         void assign(int t, int n, double f) { this->to = t; next = n; this->flow = f; }
     17     };
     18     struct Iter //迭代从节点v出发的所有边
     19     {
     20         EList *par;
     21         int e;
     22         Iter& operator ++ () { e = par->elist[e].next; return *this; }
     23         bool operator != (const Iter& rhs) const { return e != rhs.e; }
     24         //operator*返回的是引用类型
     25         Edge& operator * () const { return par->elist[e]; } //只保留基类部分,不含next
     26         Edge* operator -> () const { return par->elist + e; }
     27         //(适用于网络流等问题)返回反向边
     28         Edge& inv() const { return par->elist[e ^ 1]; }
     29     };
     30     struct View //用于遍历从某个节点出发的所有边的视图,配合C++11的range-based for使用
     31     {
     32         EList *par;
     33         int v;
     34         Iter begin() const { return { par, par->head[v] }; }
     35         Iter end() const { return { par, -1 }; } //用-1表示结束
     36     };
     37 
     38     Node elist[E];
     39     int head[V];
     40     int ecnt;
     41 
     42     void init()
     43     {
     44         FILL(head, -1); //用-1表示结束
     45         ecnt = 0;
     46     }
     47     void add(int u, int v, double f)
     48     {
     49         elist[ecnt].assign(v, head[u], f);
     50         head[u] = ecnt++;
     51         elist[ecnt].assign(u, head[v], 0.0);
     52         head[v] = ecnt++;
     53     }
     54     //返回从v节点出发的所有边的视图
     55     View view(int v) { return { this, v }; } //如果编译错误,试着将this指针const_cast一下
     56     //迭代从v出发的所有边的另一种方式,对于网络流等需要访问反向边的场合,View就不太适合了
     57     Iter begin(int v) { return { this, head[v] }; }
     58     Iter end(int v = -1) { return { this, -1 }; }
     59 };
     60 
     61 int R, C, N, S, T;
     62 EList<110, 123456> elist;
     63 
     64 void input()
     65 {
     66     scanf("%d%d%d", &R, &C, &N);
     67     elist.init();
     68     S = 0;
     69     T = R + C + 1;
     70 
     71     for (int i = 1; i <= R; i++)
     72     {
     73         double v;
     74         scanf("%lf", &v);
     75         elist.add(S, i, log2(v));
     76     }
     77     for (int i = 1; i <= C; i++)
     78     {
     79         double v;
     80         scanf("%lf", &v);
     81         elist.add(i + R, T, log2(v));
     82     }
     83 
     84     for (int u, v, i = 1; i <= N; i++)
     85     {
     86         scanf("%d%d", &u, &v);
     87         elist.add(u, R + v, 1e50);
     88     }
     89 }
     90 
     91 int layer[110];
     92 std::queue<int> que;
     93 
     94 bool bfs()
     95 {
     96     FILL(layer, -1);
     97     que.push(S);
     98     layer[S] = 0;
     99 
    100     while (!que.empty())
    101     {
    102         int cur = que.front();
    103         que.pop();
    104 
    105         for (auto& e: elist.view(cur))
    106         {
    107             if (layer[e.to] == -1 && e.flow > 0.0)
    108             {
    109                 layer[e.to] = layer[cur] + 1;
    110                 que.push(e.to);
    111             }
    112         }
    113     }
    114     return layer[T] != -1;
    115 }
    116 
    117 //#include <fmt/format.h>
    118 //using namespace fmt::literals;
    119 
    120 double dfs(int cur, double rem)
    121 {
    122     if (cur == T)
    123         return rem;
    124 
    125     double origRem = rem;
    126     for (auto it = elist.begin(cur); rem > 0.0 && it != elist.end(); ++it)
    127     {
    128         if (layer[it->to] == layer[cur] + 1 && it->flow > 0.0)
    129         {
    130             double n = dfs(it->to, std::min(rem, it->flow));
    131             //fmt::print("cur = {cur}, to = {to}, flow = {flow:.3f}
    ", "cur"_a = cur, "to"_a = it->to, "flow"_a = n);
    132             rem -= n;
    133             it->flow -= n;
    134             it.inv().flow += n;
    135         }
    136     }
    137     return origRem - rem;
    138 }
    139 
    140 double solve()
    141 {
    142     double ans = 0.0;
    143     while (bfs())
    144         ans += dfs(S, 100);
    145     return pow(2.0, ans);
    146 }
    147 
    148 int main()
    149 {
    150     int T;
    151     for (scanf("%d", &T); T; T--)
    152     {
    153         input();
    154         printf("%.4f
    ", solve());
    155     }
    156     return 0;
    157 }
  • 相关阅读:
    起泡排序引申出的问题
    关于NPC和NP-Hard问题
    我的书单(更新中)
    OpenCV2学习笔记03:Qt中配置OpenCV环境
    Ubuntu 14.04为浏览器添加Flash插件
    CSS3基础
    HTML5进阶
    拖拽上传及读取文件实现
    生产者消费者模型
    进程
  • 原文地址:https://www.cnblogs.com/Onlynagesha/p/8810568.html
Copyright © 2011-2022 走看看