[线性规划与网络流24题] 餐巾计划问题
2016-05-28 23:32
507 查看
CodeVS 1237。
粗浅地学习了带上下界的网络流的人的代码的时间开销:1472ms。
领悟二分图精髓的人的代码的时间开销:18ms。
两种建图的顶点数相同,我的边数是后者的2/3。但是增广的次数可能很多。
上面的分析中,我们区分了“今天使用的”和“今天清洗的”。把“今天清洗的”作为X集合,“今天使用的”作为Y集合,我们能够建立二分图模型。今天使用的=今天清洗的=今天的需求,S->Xi,Yi->T,容量为ri,费用为0,求最大流把它们流满即满足约束。它们必能满流,因为其他边的容量都是正无穷的,见下文。
新买的:S->Yi,容量inf,费用p。
快洗:Xi->Y(i+m),i+m<=N,容量inf,费用f。
慢洗:Xi->Y(i+n),i+n<=N,容量inf,费用s。
延期处理:Xi->X(i+1),i< N,容量inf,费用0。
就是这样。
这个模型使我欣赏的地方:
1. 通过分析,把>=转为=,由于流的容量限制(<=),流量最大时满足了约束(取得等号)。“最小费用”、“最大流”两个约束很好地统一了。
2. “从源点流出的=流入汇点的”——广义的流量平衡。平时我们讲流量平衡,都是以一个顶点为对象,流入=流出。别忽视了整体。
每个点i拆成两个,中间连边,容量为ri,费用为0。即in(i)->out(i)。
S->in(i),容量inf,费用为p。
out(i)->in(i+m),i+m<=N,容量inf,费用为f。
out(i)->in(i+n),i+n<=N,容量inf,费用s。
out(i)->out(i+1),i< N,容量inf,费用0。
out(N)->T,容量inf,费用为0。
我拆点不是看到了二分图,而是为了给节点赋容量。
对比两种模型,我的out很像二分图里的X,in很像二分图里的Y。实际上,用上下界网络流的方法,分离必要弧,建立附加源S’、汇T’,连接原T->S,两个模型是等价的。S’->S会经过两条容量为inf,费用为0的边,费用p相当于由S’付。但是,由于没有理清对第1点里“容量”的真正要求,一直觉得得求最小/大流,我使用了以下非常规方法。
第1点的“容量”,现在想来,我实际想表述的是上界=下界=ri。之前没意识到,直接跑S-T最小费用最大流,企图让这些边流满。然而流满它们的方式很多,最大流等于r1+r2+…+rN,不是我想要的。这里,“最小(可行)费用”和“最大流”未能统一。
做了些修改。用带上下界的网络流的方法,先跑S-T可行流,再跑T-S最大流。我直接建了跑完S-T可行流后的残存网络(去掉了一些不必要的弧)。接下来跑T-S最小费用最大流吗?好像不对头。这样求出了S-T最小流,但流量最小并不意味着费用最小。试了试p=1的情形,果然给出了错误的答案。
这里的费用有正有负。一开始,流费用为负;随着增广的进行,费用逐渐变成正的。也许我该在费用变为正数的时候停止增广。忽然想到了什么——
流量不固定的s-t最小费用流。如果网络中的费用有正有负,如何求s-t最小费用流?注意,这里的流量并不固定。
解:如果费用都是正的,最小费用流显然是零流;但由于负费用的存在,最短增广路的权值可能是负的,这样增广之后会得到更小的费用;但随着增广的进行,增广路权值逐渐增大,最后变成正数,此时应该停止增广。换句话说,最小费用随着流量增大先减小,后增大,成下凸函数。前面说过,下凸函数求最小值一般使用三分法,但这里可不用这么麻烦,只需在最短增广路费用为正时停止增广即可,三分反而比较慢(想一想,为什么)。需要注意的是,如果一开始不仅有负费用弧,还有负费用圈,必须先用消圈法消去负圈,否则最短增广路算法的前提不成立。当然,如果网络是无环的,则无此问题。——刘汝佳《算法竞赛入门经典——训练指南》
正所谓“纸上得来终觉浅,绝知此事要躬行”。
这个模型虽然在边数上有优势,但在实践中运行得慢。大概是增广次数多。这种求最小费用流的最短增广路算法,缺点在于每次只能增广一条路。也许该学学zkw费用流了,很想知道它怎样做出改进。
点评:尽管结果不爽,但过程很爽。又实践了一次自创的“直接建可行流的残存网络法”,效果不如上次解决“最小路径覆盖问题”。要让一些边满流,把它们跟源、汇相连。
粗浅地学习了带上下界的网络流的人的代码的时间开销:1472ms。
领悟二分图精髓的人的代码的时间开销:18ms。
两种建图的顶点数相同,我的边数是后者的2/3。但是增广的次数可能很多。
优雅的模型
约束是每天的餐巾够用,目标是使费用最小。每天的餐巾有三种来源:新买的,m天前送到快洗部的,n天前送到慢洗部的。每天的餐巾有三种去路:延期处理,送到快洗部,送到慢洗部。网络流模型擅长处理的是小于等于号,然而这里是“够用”即大于等于。(1) 如果总是存在一种刚好够用的方案,它显然优于其他有冗余的方案。今天多用一些餐巾的好处是能多洗一些餐巾以备后用……这是不必要的。今天多用的餐巾如果是买的,要用的那天再买,能省去清洗费用;如果是洗的,从它被使用的那天起延期处理,今天清洗即可,今天用不着使用它。(2) 刚好够用的方案总是存在。所以,根据(1)(2),在费用最小的目标下,“够用”可以改成“恰好够用”。上面的分析中,我们区分了“今天使用的”和“今天清洗的”。把“今天清洗的”作为X集合,“今天使用的”作为Y集合,我们能够建立二分图模型。今天使用的=今天清洗的=今天的需求,S->Xi,Yi->T,容量为ri,费用为0,求最大流把它们流满即满足约束。它们必能满流,因为其他边的容量都是正无穷的,见下文。
新买的:S->Yi,容量inf,费用p。
快洗:Xi->Y(i+m),i+m<=N,容量inf,费用f。
慢洗:Xi->Y(i+n),i+n<=N,容量inf,费用s。
延期处理:Xi->X(i+1),i< N,容量inf,费用0。
就是这样。
这个模型使我欣赏的地方:
1. 通过分析,把>=转为=,由于流的容量限制(<=),流量最大时满足了约束(取得等号)。“最小费用”、“最大流”两个约束很好地统一了。
2. “从源点流出的=流入汇点的”——广义的流量平衡。平时我们讲流量平衡,都是以一个顶点为对象,流入=流出。别忽视了整体。
我的模型
写完上面的,自惭形秽。每个点i拆成两个,中间连边,容量为ri,费用为0。即in(i)->out(i)。
S->in(i),容量inf,费用为p。
out(i)->in(i+m),i+m<=N,容量inf,费用为f。
out(i)->in(i+n),i+n<=N,容量inf,费用s。
out(i)->out(i+1),i< N,容量inf,费用0。
out(N)->T,容量inf,费用为0。
我拆点不是看到了二分图,而是为了给节点赋容量。
对比两种模型,我的out很像二分图里的X,in很像二分图里的Y。实际上,用上下界网络流的方法,分离必要弧,建立附加源S’、汇T’,连接原T->S,两个模型是等价的。S’->S会经过两条容量为inf,费用为0的边,费用p相当于由S’付。但是,由于没有理清对第1点里“容量”的真正要求,一直觉得得求最小/大流,我使用了以下非常规方法。
第1点的“容量”,现在想来,我实际想表述的是上界=下界=ri。之前没意识到,直接跑S-T最小费用最大流,企图让这些边流满。然而流满它们的方式很多,最大流等于r1+r2+…+rN,不是我想要的。这里,“最小(可行)费用”和“最大流”未能统一。
做了些修改。用带上下界的网络流的方法,先跑S-T可行流,再跑T-S最大流。我直接建了跑完S-T可行流后的残存网络(去掉了一些不必要的弧)。接下来跑T-S最小费用最大流吗?好像不对头。这样求出了S-T最小流,但流量最小并不意味着费用最小。试了试p=1的情形,果然给出了错误的答案。
这里的费用有正有负。一开始,流费用为负;随着增广的进行,费用逐渐变成正的。也许我该在费用变为正数的时候停止增广。忽然想到了什么——
流量不固定的s-t最小费用流。如果网络中的费用有正有负,如何求s-t最小费用流?注意,这里的流量并不固定。
解:如果费用都是正的,最小费用流显然是零流;但由于负费用的存在,最短增广路的权值可能是负的,这样增广之后会得到更小的费用;但随着增广的进行,增广路权值逐渐增大,最后变成正数,此时应该停止增广。换句话说,最小费用随着流量增大先减小,后增大,成下凸函数。前面说过,下凸函数求最小值一般使用三分法,但这里可不用这么麻烦,只需在最短增广路费用为正时停止增广即可,三分反而比较慢(想一想,为什么)。需要注意的是,如果一开始不仅有负费用弧,还有负费用圈,必须先用消圈法消去负圈,否则最短增广路算法的前提不成立。当然,如果网络是无环的,则无此问题。——刘汝佳《算法竞赛入门经典——训练指南》
正所谓“纸上得来终觉浅,绝知此事要躬行”。
这个模型虽然在边数上有优势,但在实践中运行得慢。大概是增广次数多。这种求最小费用流的最短增广路算法,缺点在于每次只能增广一条路。也许该学学zkw费用流了,很想知道它怎样做出改进。
点评:尽管结果不爽,但过程很爽。又实践了一次自创的“直接建可行流的残存网络法”,效果不如上次解决“最小路径覆盖问题”。要让一些边满流,把它们跟源、汇相连。
代码
#include <cstdio> #include <queue> using namespace std; const int MAXN = 1000, MAXV = MAXN*2+2, MAXE = MAXN*8, INF = 1<<30; int e_ptr = 2, N, p, m, f, n, s; int fst[MAXV+5]; struct Edge { int v, next, c, w; } E[MAXE+5]; inline void add_edge(int u, int v, int c, int w) { E[e_ptr] = (Edge){v, fst[u], c, w}; fst[u] = e_ptr++; E[e_ptr] = (Edge){u, fst[v], 0, -w}; fst[v] = e_ptr++; } namespace MCMF { int d[MAXV+5], p[MAXV+5]; bool inq[MAXV+5]; bool SPFA(int s, int t, int& cost) { queue<int> Q; for (int i = s+1; i <= t; ++i) { inq[i] = false; d[i] = INF; } inq[s] = true; d[s] = 0; Q.push(s); while (!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = false; for (int i = fst[u]; i; i = E[i].next) { int v = E[i].v; if (E[i].c > 0 && d[v] > d[u] + E[i].w) { d[v] = d[u] + E[i].w; p[v] = i^1; if (!inq[v]) { inq[v] = true; Q.push(v); } } } } if (d[t] == INF) return false; int f = INF; for (int u = t; u != s; u = E[p[u]].v) f = min(f, E[p[u]^1].c); for (int u = t; u != s; u = E[p[u]].v) { E[p[u]].c += f; E[p[u]^1].c -= f; cost += f*E[p[u]^1].w; } return true; } int MCMF(int s, int t) { int cost = 0, d_cost = 0; while (SPFA(s, t, d_cost)) if (d_cost < 0) { cost += d_cost; d_cost = 0; } else break; return cost; } } int main() { scanf("%d %d %d %d %d %d", &N, &p, &m, &f, &n, &s); int sum = 0; for (int i = 1; i <= N; ++i) { int r; scanf("%d", &r); sum += r; add_edge((i<N ? 2*i+1 : 0), 2*i-1, sum, 0); if (i+m <= N) add_edge(2*i-1, 2*(i+m), INF, f); if (i+n <= N) add_edge(2*i-1, 2*(i+n), INF, s); add_edge(2*i, 2*N+1, r, -p); } printf("%d", p*sum + MCMF::MCMF(0, 2*N+1)); return 0; }
相关文章推荐
- Http协议应用
- http 状态码大全
- Java网络编程(二) - 15分钟搭建我的第一个 Spring MVC 项目
- 网络及UIWebView 介绍
- android网络框架OkHttp之get请求(源码初识)
- CS231n 卷积神经网络与计算机视觉 7 神经网络训练技巧汇总 梯度检验 参数更新 超参数优化 模型融合 等
- eNSP组网设计与配置实验
- windows下apache配置ssl(https)服务器
- Java网络编程
- Hyper-V网络配置
- python:网络爬虫入门经验总结大大大大全
- spring mvc框架整个执行过程,从输入http url,到action映射,再到action处理,返回jsp文件,解析jsp文件,然后渲染, 到返回给浏览器展示结果
- Sflow介绍
- Sublime Text 3 配置分析 --摘录于http://www.educity.cn/wenda/150004.html
- Httpclient远程调用WebService示例(Eclipse+httpclient
- 一个好用的网络图片下载工具类ImageLoader (LruCache一级缓冲机制)
- [置顶] [CSAPP笔记][第十一章网络编程]
- 《java入门第一季》之Socket编程通信和TCP协议通信图解
- 《java入门第一季》之Socket编程通信和TCP协议通信图解
- [CSAPP笔记][第十一章网络编程]