有\(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. 20155323 2016-2017-2 《Java程序设计》第10周学习总结

    20155323 2016-2017-2 <Java程序设计>第10周学习总结 教材学习内容总结 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据. 狭义的网络编程范畴就是程序 ...

  2. 20155327 嵌入式C语言课堂补交

    嵌入式C语言 题目要求 在作业本上完成附图作业,要认真看题目要求. 提交作业截图 作弊本学期成绩清零(有雷同的,不管是给别人传答案,还是找别人要答案都清零) 题目分析 分析一:提取插入时间 根据老师上 ...

  3. WEB项目日志分析系统思考

    一.为什么需要日志分析系统 对ETL系统中数据转换和存储操作的相关日志进行记录以及实时分析有助于我们更好的观察和监控ETL系统的相关指标(如单位时间某些操作的处理时间),发现系统中出现的缺陷和性能瓶颈 ...

  4. 苏州Uber优步司机奖励政策(4月18日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  5. macOS 10.14 Mojave 开发环境配置Apache多PHP版本

    第1部分:macOS 10.14 Mojave Web开发环境 在macOS上开发Web应用程序真是一种乐趣.设置开发环境有很多选择,包括广受欢迎的MAMP Pro,它在Apache,PHP和MySQ ...

  6. jQuery Validate (登录页面相关验证)

    $(function() { var submit = false; var superHtml = []; /** * 匹配企业帐号,以字母开头,长度在6-20之间,只能包含字符.数字和下划线. * ...

  7. uvaoj 156Ananagrams(map和vector组合使用)

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  8. java之接口开发-初级篇-http和https

    http协议util address(url地址),str(数据参数) private static HttpMethod getPostJsonMethodInRequestBody(String ...

  9. C++进阶训练——停车收费系统设计

    一.简介 经过一段时间的c++基础学习,是时候做一个较为全面的.运用c++功能的较复杂的项目练练手了. 运用软件:Visual Studio   (VS). 题目:c++停车收费系统设计(某本编程书进 ...

  10. php-fpm配置

    [global] error_log = /letv/log/php-fpm_error.log [www] user = apache group = apache listen = 127.0.0 ...