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 }
原文地址:https://www.cnblogs.com/Onlynagesha/p/8810568.html