【BZOJ1061】【codevs1803】志愿者招募,神奇建图费用流
来源:互联网 发布:mac webpack 安装 编辑:程序博客网 时间:2024/05/05 16:16
传送门1
传送门2
写在前面:第一次写成功的费用流是个神奇数学建模题……
思路:摘自http://www.ithao123.cn/content-4207689.html,感觉这个要比列不等式+松弛操作的说法更加明白简单
设每个时间i都需要有至少Ai个志愿者,设每种志愿者i使用了xi个,那么我们对于每个时间点都可以列出一个不等式:x1+x2+x3+…+xn>=Ai(其中如果第i类志愿者不能在该区间工作则xi固定为0)。
最后要求最小化w1*x1+w2*x2+x3*x3+…+wn*xn(其中wi是第i种志愿者的单位价格)。
这正是一个线性规划的问题。
那么我们是否可以转化为网络流来求解呢?
当然是可以哒~
现在我们将每个点的Ai取反,变成-Ai,这样他就成了一个“坑”了(相对于y=0而言),我们从源点S给最左边的时间点引流,流的大小为U(U为大整数),然后每个时间点i向时间点i+1建边(i,i+1,U-Ai,0),最后设汇点为最右端的时间点。
现在,如果所有时间点的Ai都为0,那么显然,汇点的流量恰好等于U。
问题来了,现在我们有的Ai不等于0了,那么显然源点到汇点的流量被这些“坑”所截断了,怎么解决这一个问题?
假设志愿者工作的时间为【Li,Ri】,且该种志愿者单位花费为Ci,则我们建边(Li,Ri+1,INF,Ci),表示我们雇佣了一些志愿者“填坑”来了,如果雇佣了xi个志愿者,则说明将该区间内的所有“坑”的深度填掉了xi(当然可能有的坑在“填坑”行动后高于y=0,那也无所谓了嘛,多多益善~)。
那么现在是不是看起来思路有一点清晰了?
于是志愿者的作用就是一个人可以填一个区间的“坑”(好厉害!),然后需要每种志愿者选择一些使得花费最小的情况下填掉所有的“坑”,就是这样~
最后就是让费用流帮我们选择志愿者的时候了~
于是我们按照上面的方法跑完一遍最小费用最大流,如果流量等于U,则说明满流(志愿者们成功填掉了所有的坑,同志们辛苦了~),此时的记录的cost就是最小值(cost为算法记录的最小费用)。如果流量不等于U则无解。
这时我们又收获了一种费用流的模型:初始给一道大流,然后将有至少覆盖次数限制的点(边)的权值取反变成“坑”,最后区间覆盖就等于“填坑”,只要最后的流量等于大流的流量,就有解。
注意:
1.建图不要想当然,一定要遵从流量守恒等原则,最好在纸上画画写写,并手动跑一遍
2.小心int溢出,这个好坑的
代码:
#include"bits/stdc++.h"#define Maxn 0x7fffffff#define LL long longusing namespace std;int tot=1,s,t,n,m,up[20010];bool flag[20010];LL ans,dis[20010];int person[1010],start[10010],end[10010],c[10010],first[20010];struct os{ int fa,son,next; LL limit,cost;}a[100000];queue<int> q;void add(LL x,LL y,LL z,LL co){ a[++tot].fa=x; a[tot].son=y; a[tot].limit=z; a[tot].cost=co; a[tot].next=first[x]; first[x]=tot;}bool spfa(){ memset(dis,127,sizeof(dis)); memset(up,0,sizeof(up)); dis[s]=0; flag[s]=1; q.push(s); LL k; while (!q.empty()) { k=q.front(); for (LL i=first[k];i;i=a[i].next) if (a[i].limit&&dis[a[i].son]>dis[k]+a[i].cost) { dis[a[i].son]=dis[k]+a[i].cost; up[a[i].son]=i; if (!flag[a[i].son]) flag[a[i].son]=1,q.push(a[i].son); } q.pop(); flag[k]=0; } if (dis[t]<0x7fffff) return 1; else return 0;}LL flow(){ LL ans=0,minn=0x7fffffff; for (int i=up[t];i;i=up[a[i].fa]) minn=min(minn,a[i].limit); if (minn==Maxn) return 0; for (int i=up[t];i;i=up[a[i].fa]) ans+=minn*a[i].cost, a[i].limit-=minn, a[i^1].limit+=minn; return ans;}main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&person[i]); for (int i=1;i<=m;i++) scanf("%d%d%d",&start[i],&end[i],&c[i]); s=n+2; t=n+1; add(s,1,Maxn,0); add(1,s,0,0); for (int i=1;i<=n;i++) add(i,i+1,Maxn-person[i],0), add(i+1,i,Maxn,0); for (int i=1;i<=m;i++) add(start[i],end[i]+1,Maxn,c[i]), add(end[i]+1,start[i],0,-c[i]); while (spfa()) ans+=flow(); printf("%lld",ans);}
- 【BZOJ1061】【codevs1803】志愿者招募,神奇建图费用流
- bzoj1061: [Noi2008]志愿者招募 费用流
- [BZOJ1061]NOI2008志愿者招募|费用流|线性规划
- 【费用流】[BZOJ1061]/[HYSBZ1061]志愿者招募
- [BZOJ1061][NOI2008]志愿者招募 费用流
- bzoj1061志愿者招募 费用流or单纯形
- [BZOJ1061] [NOI2008] 志愿者招募 - 最小费用最大流
- 【费用流|单纯形】BZOJ1061 [Noi2008]志愿者招募
- [线性规划 费用流]BZOJ1061 志愿者招募 && BZOJ3112防守战线
- bzoj1061: [Noi2008]志愿者招募(最小费用最大流)
- bzoj1061 [Noi2008]志愿者招募(线性规划/费用流)
- 【BZOJ1061】【NOI2008】志愿者招募 费用流神题、单纯形裸题(代码费用流)
- [BZOJ1061][Noi2008]志愿者招募
- bzoj1061【NOI2008】志愿者招募
- 【NOI2008】【BZOJ1061】志愿者招募
- bzoj1061: [Noi2008]志愿者招募
- 【NOI2008】BZOJ1061志愿者招募
- [bzoj1061][NOI2008]志愿者招募
- TOJ 2548.Celebrity jeopardy
- SVN更新错误:类加载失败,无法读取项目文件"web.csproj"
- Programming Exercise 7:K-means Clustering and Principal Component Analysis (第二部分PCA)
- Struts2中动态方法调用有三种方式
- WDF驱动学习:驱动对象,设备对象(一)
- 【BZOJ1061】【codevs1803】志愿者招募,神奇建图费用流
- hdu1573X问题 同余
- 第3周项目4 谁是小偷
- CodeForces 630M:Turn【数学】
- UML之类图
- [CAAnimation核心动画练习四]饼图1.5 增加值的显示
- DOM或BOM区别
- C++ 虚函数表解析
- [POJ2311]Cutting Game(博弈Multi-SG函数)