loj 6008 餐巾计划 - 费用流

题目传送门

  传送门

题目大意

  (经典题还不知道题意?)

  容易想到需要把未使用的餐巾和已经使用的餐巾分开。

  设$X_i$表示第$i$天已经的使用餐巾的点,设$Y_i$表示第$i$天还未使用的餐巾的点

  我们知道使用过的餐巾数量 = 洗出来的餐巾数量 + 购买的餐巾数量(一个餐巾被多次洗出来算多次)。

  右边是啥,我们不清楚,但是我们清楚每一天新增的使用过的餐巾的数量,所以源点向$X_i$连一条容量为$r_i$,费用为0的边。

  接下来还有几种连边:

  • $X_i$向$X_{i + 1}$连一条容量为$\infty$,费用为0的边(使用过的餐巾还是使用过的,$i < n$)。
  • $Y_i$向$Y_{i + 1}$连一条容量为$\infty$,费用为0的边
  • $X_i$向$Y_{i + M}$连一条容量为$\infty$,费用为$F$的边
  • $X_i$向$Y_{i + N}$连一条容量为$\infty$,费用为$S$的边

  对于第$i$天会用掉$r_i$的餐巾,所以$Y_i$向汇点连一条容量为$r_i$,费用为0的边。

  还有一个购买餐巾,它直接会变成这一天的未使用过的餐巾,所以原点向$Y_i$连一条容量为$\infty$,费用为$p$的边。

Code

  1 /**
  2  * loj
  3  * Problem#6008
  4  * Accepted
  5  * Time: 701ms
  6  * Memory: 636k
  7  */
  8 #include <iostream>
  9 #include <cstdlib>
 10 #include <cstdio>
 11 #include <queue>
 12 using namespace std;
 13 typedef bool boolean;
 14 
 15 template <typename T>
 16 void pfill(T* pst, const T* ped, T val) {
 17     for ( ; pst != ped; *(pst++) = val);
 18 }
 19 
 20 typedef class Edge {
 21     public:
 22         int ed, nx, r, c;
 23 
 24         Edge(int ed = 0, int nx = 0, int r = 0, int c = 0) : ed(ed), nx(nx), r(r), c(c) {    } 
 25 } Edge;
 26 
 27 typedef class MapManager {
 28     public:
 29         int* h;
 30         vector<Edge> es;
 31 
 32         MapManager() {    }
 33         MapManager(int n) {
 34             h = new int[(n + 1)];
 35             pfill(h, h + n + 1, -1);
 36         }
 37 
 38         void addEdge(int u, int v, int r, int c) {
 39             es.push_back(Edge(v, h[u], r, c));
 40             h[u] = (signed) es.size() - 1;
 41         }
 42 
 43         void addArc(int u, int v, int cap, int c) {
 44             addEdge(u, v, cap, c);
 45             addEdge(v, u, 0, -c);
 46         }
 47 
 48         Edge& operator [] (int p) {
 49             return es[p];
 50         }
 51 } MapManager;
 52 
 53 const signed int inf = (signed) (~0u >> 1);
 54 
 55 class Graph {
 56     public:
 57         int S, T;
 58         MapManager g;
 59         
 60         int *le;
 61         int *f, *mf;
 62         boolean *vis;
 63 
 64         // be sure T is the last node
 65         void set(int S, int T) {
 66             this->S = S;
 67             this->T = T;
 68             f = new int[(T + 1)];
 69             le = new int[(T + 1)];
 70             mf = new int[(T + 1)];
 71             vis = new boolean[(T + 1)];
 72             pfill(vis, vis + T, false);
 73         }
 74 
 75         int spfa() {
 76             queue<int> que;
 77             pfill(f, f + T + 1, inf);
 78             que.push(S);
 79             f[S] = 0, le[S] = -1, mf[S] = inf;
 80             while (!que.empty()) {
 81                 int e = que.front();
 82                 que.pop();
 83                 vis[e] = false; 
 84                 for (int i = g.h[e], eu, w; ~i; i = g[i].nx) {
 85                     if (!g[i].r)
 86                         continue;
 87                     eu = g[i].ed, w = f[e] + g[i].c;
 88                     if (w < f[eu]) {
 89                         f[eu] = w, le[eu] = i, mf[eu] = min(mf[e], g[i].r);
 90                         if (!vis[eu]) {
 91                             vis[eu] = true;
 92                             que.push(eu);
 93                         }
 94                     }
 95                 }
 96             }
 97             if (f[T] == inf)
 98                 return inf;
 99             int rt = 0;
100             for (int p = T, e; ~le[p]; p = g[e ^ 1].ed) {
101                 e = le[p];
102                 g[e].r -= mf[T];
103                 g[e ^ 1].r += mf[T];
104                 rt += mf[T] * g[e].c;
105             }
106             return rt;
107         }
108 
109         int min_cost() {
110             int rt = 0, delta;
111             while ((delta = spfa()) != inf) {
112                 rt += delta;
113 //                cerr << delta << '\n';
114             }
115             return rt;
116         }
117 } Graph;
118 
119 int n;
120 int P, M, F, N, S;
121 int *require;
122 MapManager &g = Graph.g;
123 
124 inline void init() {
125     scanf("%d", &n);
126     scanf("%d%d%d%d%d", &P, &M, &F, &N, &S);
127     require = new int[(n + 1)];
128     g = MapManager(n << 1 | 1);
129     for (int i = 1; i <= n; i++) {
130         scanf("%d", require + i);
131     }
132 }
133 
134 inline void solve() {
135     int T = n << 1 | 1;
136     Graph.set(0, T);
137     for (int i = 1; i <= n; i++) {
138         g.addArc(0, i, require[i], 0);
139         g.addArc(0, i + n, inf, P);
140         if (i < n) {
141             g.addArc(i, i + 1, inf, 0);
142             g.addArc(i + n, i + n + 1, inf, 0);
143         }
144         if (i + M <= n)
145             g.addArc(i, i + n + M, inf, F);
146         if (i + N <= n)
147             g.addArc(i, i + n + N, inf, S);
148         g.addArc(i + n, T, require[i], 0);
149     }
150     printf("%d\n", Graph.min_cost());
151 }
152 
153 int main() {
154     init();
155     solve();
156     return 0;
157 }

转载于:https://www.cnblogs.com/yyf0309/p/10193319.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值