有\(n\)天,\(m\)类志愿者,一个第\(i\)类志愿者可以从第\(s_i\)天工作到第\(t_i\)天,第\(i\)天工作的志愿者不少于\(b_i\)个。每一类志愿者都有单价\(c_i\),问满足要求的最小花费。

分析

它其实是这样一个问题:

\[\text{Minimize }CX \\
AX\ge B \\
X\ge 0 \\
A_{ij}=\begin{cases}
1 && s_i\le j\le t_i \\
0 && \text{otherwise}
\end{cases}
\]

这是一个线性规划问题。

单纯形法

线性规划的一种通用解法是单纯形法,复杂度为指数级,但一般情况下(不去卡它)效率十分令人满意。

要使用单纯形法,我们需要把线性规划转化成一般形式:

\[\text{Maximize } CX \\
AX\le B \\
X\ge 0
\]

然而这里的问题是,我们的线性规划是一个最小化问题,用的都是大于号,不是标准形式。这时候需要用强对偶定理转化这个问题。

如果一个线性规划有最优解,那么它的对偶问题也有最优解,并且最优解相同。

一个标准型对偶问题如下:

\[\text{Minimize } B^TX \\
A^TX\ge C^T \\
X\ge 0
\]

单纯形方法的意义在于在\(m\)维空间中从初始点不断跳到一个相邻的更高点。

有一个需要注意的地方,例如我们要最大化\(CX\),那么我们其实是把它变成\(P=CX\),然后变成\(-CX+P=0\)来解,所以要取负号。

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxm=1e4+10;
const int maxn=1e3+10;
const double inf=1e300;
double a[maxm][maxn];
int n,m;
void Simplex() {
bool flag;
do {
flag=false;
for (int col=1;col<=n;++col) if (a[m+1][col]<0) {
int row=0;
double mi=inf;
for (int r=1;r<=m;++r) if (a[r][col]>0){
double p=a[r][n+1]/a[r][col];
if (p<0) continue; else if (p<mi) mi=p,row=r;
}
if (row) flag=true; else continue;
for (int i=1;i<=m+1;++i) if (i!=row && a[i][col]!=0) {
double p=a[i][col]/a[row][col];
for (int j=1;j<=n+1;++j) if (j==col) a[i][j]*=-1; else a[i][j]-=a[row][j]*p;
}
break;
}
} while (flag);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
n=read(),m=read();
for (int i=1;i<=n;++i) a[m+1][i]=-read();
for (int i=1;i<=m;++i) {
int s=read(),t=read();
a[i][n+1]=read();
for (int j=s;j<=t;++j) a[i][j]=1;
}
Simplex();
printf("%d\n",(int)a[m+1][n+1]);
return 0;
}

网络流

其实可以发现,这个问题有一个非常特别的地方,就是变量是连续出现的。

每个不等式是一个大于等于的形式,所以我们给每个不等式减掉一个\(y_i\),使其变成等于。\(Y\ge 0\) 。

第\(i\)天的等式我们用\(P_i\)表示,加上\(P_0=P_{n+1}=0\),可以发现,如果写出\(W_i=P_i-P_{i-1},i\in[1,n+1]\),那么这样的每个式子中每个\(x_i,y_i\)都只出现了一次,一次是加一次是减。我们把常数移到左边,那么所有等式的右边都是0,左边含有一些变量和一个常数,可能为正的或负的。

这很像一个网络流的流量平衡!

把每个\(W_i\)看成网络中的一个点,新加源点和汇点,如果\(W_i\)中一个变量是负的,\(W_j\)中一个变量是正的,那么我们连边\((i,j)\),容量为无穷大。如果这个变量是\(x\),那么费用为\(c_i\),否则费用为0。如何处理常数呢?我们发现,如果一个常数是正的,那么就相当于从源点流进来的流量,如果是负的那就是这个点流去汇点的流量。

这样我们就完成了每个点的流量平衡,而且一个很重要的性质就是正常数之和与负常数之和的绝对值是相等的(从相减的过程可以得出),所以如果我们对这个网络求最大流,能够流满,那么所有条件都可以得到满足。

又因为我们要求的是最小费用,所以最小费用最大流的费用就是答案啦。

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=1e3+10;
const int maxm=1e4+10;
const int maxb=(maxn+maxn+maxm)<<1;
const int inf=2147483647;
int n,m,need[maxn],que[maxb],ql,qr,d[maxn],from[maxn];
bool inq[maxn];
struct graph {
struct edge {
int u,v,w,cost,nxt;
} e[maxb];
int tot,h[maxn];
graph ():tot(1) {}
void add(int u,int v,int w,int cost=0) {
e[++tot]=(edge){u,v,w,cost,h[u]};
h[u]=tot;
e[++tot]=(edge){v,u,0,-cost,h[v]};
h[v]=tot;
}
bool spfa(int s,int t,int &flow,int &cost) {
que[ql=qr=1]=s;
memset(inq,0,sizeof inq);
memset(from,0,sizeof from);
inq[s]=true;
fill(d+s,d+t+1,inf);
d[s]=0;
while (ql<=qr) {
int x=que[ql++];
for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (e[i].w && d[x]+e[i].cost<d[v]) {
d[v]=d[x]+e[i].cost;
from[v]=i;
if (!inq[v]) que[++qr]=v,inq[v]=true;
}
inq[x]=false;
}
if (d[t]==inf) return false;
int mi=inf;
for (int i=from[t];i;i=from[e[i].u]) mi=min(mi,e[i].w);
flow+=mi,cost+=d[t]*mi;
for (int i=from[t];i;i=from[e[i].u]) e[i].w-=mi,e[i^1].w+=mi;
return true;
}
int mcmf(int S,int T) {
int flow=0,cost=0;
while (true) {
if (!spfa(S,T,flow,cost)) break;
}
return cost;
}
} G;
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
n=read(),m=read();
int S=0,T=n+2,ln=0,nn=0;
for (int i=1;i<=n;++i) {
nn=read();
int x=ln-nn;
if (x<0) G.add(i,T,-x); else G.add(S,i,x);
ln=nn;
}
G.add(S,n+1,ln);
for (int i=1;i<=n;++i) G.add(i,i+1,inf);
for (int i=1;i<=m;++i) {
int s=read(),t=read(),c=read();
G.add(t+1,s,inf,c);
}
printf("%d\n",G.mcmf(S,T));
return 0;
}

bzoj1061-[Noi2008]志愿者招募-单纯形 & 费用流的更多相关文章

  1. [BZOJ1061][Noi2008]志愿者招募 线性规划+费用流

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1061 根据题意列方程,然后用网络流解线性规划. 题解直接贴ByVoid的吧,太神了:htt ...

  2. 【BZOJ】1061: [Noi2008]志愿者招募(费用流+数学)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1061 好神的一题! 学会了一种建模方式: 当方程组内的任意变量都在其中两个方程出现且一正一负,可以建 ...

  3. BZOJ 1061 [Noi2008]志愿者招募(费用流)

    题目描述 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i ...

  4. NOI2008 志愿者招募 (费用流)

    题面 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i 天至 ...

  5. [BZOJ1061][Noi2008]志愿者招募

    [BZOJ1061][Noi2008]志愿者招募 试题描述 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难 题:为即将启动的奥运新项目招募一批短期志愿 ...

  6. 网络流解线性规划问题 BZOJ1061: [Noi2008]志愿者招募

    线性规划定义: 在给定有限的资源和竞争约束情况下,很多问题都可以表述为最大化或最小化某个目标.如果可以把目标指定为某些变量的线性函数,而且如果可以将资源约束指定为这些变量的等式或不等式,则得到了一个线 ...

  7. 【费用流】BZOJ1061: [Noi2008]志愿者招募(这题超好)

    1061: [Noi2008]志愿者招募 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 5291  Solved: 3173[Submit][Stat ...

  8. [BZOJ1061] [Noi2008] 志愿者招募 (费用流)

    Description 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难 题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能 ...

  9. 线性规划费用流解法(Bzoj1061: [Noi2008]志愿者招募)

    题面 传送门 Sol 线性规划费用流解法用与求解未知数为非负数的问题 这道题可以列出一堆形如 \(x[i]+x[j]+x[k]+...>=a[p]\) 的不等式 我们强行给每个式子减去一个东西, ...

随机推荐

  1. mysql的启动,停止与重启

    启动mysql:方式一:sudo /etc/init.d/mysql start 方式二:sudo start mysql方式三:sudo service mysql start 停止mysql:方式 ...

  2. Linux 下 的 Oracle,如何安装 tnsname

    运行 netca 即可:

  3. (转) 转换Drupal7模块到Drupal8

    转载地址:http://verynull.com/2015/11/02/Converting-7-x-modules-to-8-x/ 本节主要介绍如何把drupal7的模块转化为drupal8.参考资 ...

  4. jquery.validate使用 - 5

    一些常用的验证脚本 不会写js了,只能从网上找一些常用的验证脚本. // 手机号码验证jQuery.validator.addMethod("mobile", function(v ...

  5. 强化学习读书笔记 - 12 - 资格痕迹(Eligibility Traces)

    强化学习读书笔记 - 12 - 资格痕迹(Eligibility Traces) 学习笔记: Reinforcement Learning: An Introduction, Richard S. S ...

  6. HTTP简单教程

    目录 HTTP简介 HTTP工作原理 HTTP消息结构 客户端请求消息 服务器响应消息 实例 HTTP请求方法 HTTP响应头信息 HTTP状态码 HTTP状态码分类 HTTP状态码列表 HTTP c ...

  7. 使用JS验证文件类型

    项目中涉及到这一需求,在此贴出代码分享给大家, 有2中方式,一种是input中使用accept 方式 一种是使用js正则表达式判断,个人推荐使用js正则表达式,因为accept 有的浏览器并不支持,而 ...

  8. Arctic Network POJ 2349 (最小生成树思想)

    Description The Department of National Defence (DND) wishes to connect several northern outposts by ...

  9. 20172321 2018-2019《Java软件结构与数据结构》第三周学习总结

    教材学习内容总结 第五章 5.1概述 队列是一种线性集合,其元素从一端加入,从另一端删除:队列的处理方式是先进先出(First in First out). 与栈的比较(LIFO) 栈是一端操作,先进 ...

  10. Optimized Flow Migration for NFV Elasticity Control

    NFV弹性控制中的流迁移优化 ABSTRACT 基于动态创建和移除网络功能实例,NFV在网络功能控制上有很大的弹性.比如,网络功能和并,网络功能拆分,负载均衡等等. 那么为了实现弹性控制,就需要网络流 ...