BZOJ 2424 订货 最小费用流
题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=2424
题目大意:
某公司估计市场在第i个月对某产品的需求量为Ui,已知在第i月该产品的订货单价为di,上个月月底未销完的单位产品要付存贮费用m,假定第一月月初的库存量为零,第n月月底的库存量也为零,问如何安排这n个月订购计划,才能使成本最低?每月月初订购,订购后产品立即到货,进库并供应市场,于当月被售掉则不必付存贮费。假设仓库容量为S。
思路:
直接建图即可。
对于每个月i,从s->i 容量为INF,因为可以购买任意数量的产品,费用为d[i],为当月售价
从i->t容量为U[i],因为需要出售这么多,费用为0,因为出售不需要费用。
i->i+1 容量为S,因为仓库容量为S,费用为m,因为上月留下来的单位产品的费用为m
1 #include<bits/stdc++.h> 2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf 3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时 4 #define Min(a, b) ((a) < (b) ? (a) : (b)) 5 #define Mem(a) memset(a, 0, sizeof(a)) 6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1)) 7 #define MID(l, r) ((l) + ((r) - (l)) / 2) 8 #define lson ((o)<<1) 9 #define rson ((o)<<1|1) 10 #define Accepted 0 11 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} 17 while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 21 typedef long long ll; 22 const int maxn = 100 + 10; 23 const int MOD = 1000000007;//const引用更快,宏定义也更快 24 const int INF = 1e9 + 7; 25 const double eps = 1e-6; 26 27 struct edge 28 { 29 int u, v, c, f, cost; 30 edge(int u, int v, int c, int f, int cost):u(u), v(v), c(c), f(f), cost(cost){} 31 }; 32 vector<edge>e; 33 vector<int>G[maxn]; 34 int a[maxn];//找增广路每个点的水流量 35 int p[maxn];//每次找增广路反向记录路径 36 int d[maxn];//SPFA算法的最短路 37 int inq[maxn];//SPFA算法是否在队列中 38 int n, m; 39 void init(int n) 40 { 41 for(int i = 0; i <= n; i++)G[i].clear(); 42 e.clear(); 43 } 44 void addedge(int u, int v, int c, int cost) 45 { 46 e.push_back(edge(u, v, c, 0, cost)); 47 e.push_back(edge(v, u, 0, 0, -cost)); 48 int m = e.size(); 49 G[u].push_back(m - 2); 50 G[v].push_back(m - 1); 51 } 52 bool bellman(int s, int t, int& flow, long long & cost) 53 { 54 for(int i = 0; i <= n + 2; i++)d[i] = INF;//Bellman算法的初始化 55 memset(inq, 0, sizeof(inq)); 56 d[s] = 0;inq[s] = 1;//源点s的距离设为0,标记入队 57 p[s] = 0;a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的) 58 59 queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流 60 q.push(s); 61 while(!q.empty()) 62 { 63 int u = q.front(); 64 q.pop(); 65 inq[u] = 0;//入队列标记删除 66 for(int i = 0; i < G[u].size(); i++) 67 { 68 edge & now = e[G[u][i]]; 69 int v = now.v; 70 if(now.c > now.f && d[v] > d[u] + now.cost) 71 //now.c > now.f表示这条路还未流满(和最大流一样) 72 //d[v] > d[u] + e.cost Bellman 算法中边的松弛 73 { 74 d[v] = d[u] + now.cost;//Bellman 算法边的松弛 75 p[v] = G[u][i];//反向记录边的编号 76 a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量 77 if(!inq[v]){q.push(v);inq[v] = 1;}//Bellman 算法入队 78 } 79 } 80 } 81 if(d[t] == INF)return false;//找不到增广路 82 flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow 83 cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用 84 for(int u = t; u != s; u = e[p[u]].u)//逆向存边 85 { 86 e[p[u]].f += a[t];//正向边加上流量 87 e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样) 88 } 89 return true; 90 } 91 int MincostMaxflow(int s, int t, long long & cost) 92 { 93 cost = 0; 94 int flow = 0; 95 while(bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost 96 return flow;//返回最大流,cost引用可以直接返回最小费用 97 } 98 int u[maxn], b[maxn]; 99 int main() 100 { 101 int s; 102 scanf("%d%d%d", &n, &m, &s); 103 for(int i = 1; i <= n; i++)scanf("%d", &u[i]); 104 for(int i = 1; i <= n; i++)scanf("%d", &b[i]); 105 for(int i = 1; i <= n; i++) 106 { 107 addedge(0, i, INF, b[i]); 108 addedge(i, n + 1, u[i], 0); 109 } 110 for(int i = 1; i < n; i++)addedge(i, i + 1, s, m); 111 ll cost = 0; 112 MincostMaxflow(0, n + 1, cost); 113 printf("%lld\n", cost); 114 return Accepted; 115 }
越努力,越幸运