博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[网络流24题]餐巾计划问题——费用流建模
阅读量:4544 次
发布时间:2019-06-08

本文共 2944 字,大约阅读时间需要 9 分钟。

 

不错的建模题。

 

满足餐巾需求之下,花费最小。可以想到费用流。

但是怎么建模呢?

可以想到,因为N<=2000,而且一切的工作,洗刷,购买都和天有关系。

所以,肯定要把网络流中的点看做每一天。

 

比较麻烦的是,我们不好处理餐巾的干净和脏的状态,

如果每天只有“一个点”的话,我们也不能处理一个餐巾由干净变成脏的情况。(我们不能区分,一个用完的脏餐巾往下传递,和一个干净餐巾留到明天)

所以,考虑把天拆点。

 

那么,自然而然地,

一天拆成早上和晚上。

晚上会获得早上用完的脏毛巾

早上有的毛巾必须是干净的,才能用。

这样连边:

1.S向每个晚上连流ri费0,表示每天晚上会获得这么多的脏毛巾

2.每个早上向T连流ri费0,表示每天早上会用这么多的干净毛巾,并且可以限制最大流一定是sigma(ri)

3.购买,洗刷就比较自然了。

①S向每个早上,连接流inf费p,意义即买

②晚上的i,向走早上的i+day1连接流inf费cost1 ,意义即快洗部。慢洗部同理

4.还有一个情况是,可能我脏毛巾留着,等到最后要洗的时候,才拿去洗

所以,晚上i向i+1连流inf费0的边。

(当然,你也可以比较勤奋,反正以后要用,今天就先洗了。

早上i向i+1连流inf费0的边也可以。,

意义即,干净的毛巾留下来。

其实就是一个平行四边形。。

黑色是脏毛巾留下来,

红色是干净毛巾留下来。

一个提前洗,一个到时候再洗

 

这样,

我可以实现:干净毛巾转化成脏毛巾,脏毛巾洗成干净毛巾,购买干净毛巾,脏毛巾的积累。

跑费用流即可e

#include
#define reg register int#define il inline#define numb (ch^'0')using namespace std;typedef long long ll;il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x);}namespace Miracle{const int N=4005;const int M=2000*6+23;const ll inf=0x3f3f3f3f3f3f3f3fll;struct node{ int nxt,to; ll w,v;}e[2*M];int hd[N],cnt=1;int n,m,s,t;ll p,d1,c1,d2,c2;void add(int x,int y,ll z,ll c){ e[++cnt].nxt=hd[x];e[cnt].to=y; e[cnt].w=z;e[cnt].v=c; hd[x]=cnt; e[++cnt].nxt=hd[y];e[cnt].to=x; e[cnt].w=0;e[cnt].v=-c; hd[y]=cnt;}int pre[N];ll incf[N];ll d[N];queue
q;bool in[N];bool spfa(){ while(!q.empty()) q.pop(); memset(d,0x3f,sizeof d); d[s]=0; q.push(s); pre[s]=0;incf[s]=inf; while(!q.empty()){ int x=q.front();q.pop(); in[x]=0; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(e[i].w){ if(d[y]>d[x]+e[i].v){ d[y]=d[x]+e[i].v; pre[y]=i; incf[y]=min(incf[x],e[i].w); if(!in[y]){ in[y]=1; q.push(y); } } } } } if(d[t]==inf) return false; return true;}ll ans;void upda(){ int x=t; while(pre[x]){ e[pre[x]].w-=incf[t]; e[pre[x]^1].w+=incf[t]; x=e[pre[x]^1].to; } ans+=d[t]*incf[t];}int main(){ rd(n); s=2*n+1,t=2*n+2; ll x; for(reg i=1;i<=n;++i){ scanf("%lld",&x); add(s,i,x,0); add(i+n,t,x,0); } scanf("%lld%lld%lld%lld%lld",&p,&d1,&c1,&d2,&c2); for(reg i=1;i<=n;++i){ add(s,i+n,0x3f3f3f3f,p); if(i+d1<=n) add(i,i+n+d1,inf,c1); if(i+d2<=n) add(i,i+n+d2,inf,c2); if(i+1<=n) add(i,i+1,inf,0); } while(spfa()) upda(); printf("%lld",ans); return 0;}}int main(){ Miracle::main(); return 0;}/* Author: *Miracle* Date: 2018/11/28 8:21:33*/

 总结:

为什么要拆点?

因为同一个物品在不同时空下的状态是不同的,之间的决策也相对独立,甚至可能存在相互转移的情况。

当一个点表示已经无能为力的时候,可以考虑拆点。

例如修车、美食节

(化点为边,,,,严格意义上不算是,这只是为了方便统计处理,并不是状态不同)

只要能处理好拆出来的点之间的关系就好。

 

转载于:https://www.cnblogs.com/Miracevin/p/10030176.html

你可能感兴趣的文章
微服务架构下介质管理规范
查看>>
关于AutoCAD 2014的securityload…
查看>>
BM和KMP字符串匹配算法学习
查看>>
常用基本命令四(用户管理命令) - 黑猴子
查看>>
项目管理知识1
查看>>
在window环境下安装Python中的pip
查看>>
A大龙插件官方群3:621816328
查看>>
oi再见,你好明天。
查看>>
2018 Multi-University Training Contest 1 - D Distinct Values (STL+双指针)
查看>>
js学习笔记一-语法结构
查看>>
键盘对应的键值
查看>>
goLang 纳秒转 毫秒 转 英文时间格式
查看>>
微信小程序的坑坑
查看>>
图片轮播(Jquery)
查看>>
hdu 1704 Rank(floyd传递闭包)
查看>>
Educational Codeforces Round 27 G. Shortest Path Problem?(Guass异或线性基)
查看>>
【BZOJ3622】已经没有什么好害怕的了(动态规划+广义容斥)
查看>>
HDOJ 1023 Train Problem II
查看>>
途牛订单的服务化演进
查看>>
软件工程之四则运算
查看>>