有\(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. Luogu1801_黑匣子_KEY

    题目传送门 借这道题练一下Treap和Splay的板子. code: #include <cstdio> #include <cstdlib> using namespace ...

  2. string[]转换为int[]

    今天碰到一个问题,要把string[]转换为int[],但是又不想使用循环转换,找了好久最后找到了这种方法,特此记录下. string[] input = { "1", " ...

  3. HBase 第四章 HBase原理

    1  体系图 HBase中的每张表都通过行键按照一定的范围被分割成多个子表(HRegion),默认一个HRegion超过256M就要被分割成两个,这个过程由HRegionServer管理,而HRegi ...

  4. LDPC译码算法代码概述

    程序说明 V0.0 2015/1/24 LDPC译码算法代码概述   概述   本文介绍了包括LDPC_Simulation.m, ldpcdecoderbp1.m,ldpcdecoderminsum ...

  5. Jmeter4.0安装

    1.检查安装环境 1.1 JDK要求 JDK版本:1.8 1.2 检查是否安装JDK win + R 快捷键打开运行,输入 cmd 打开面板,在面板中输入 java -version,出现如下信息,即 ...

  6. TPO 02 - The Origins of Cetaceans

    TPO 02 - The Origins of Cetaceans It should be obvious that cetaceans[n. 鲸目动物]-whales, porpoises [n. ...

  7. [network]交换机中用户权限

    LEVEL 0(访问级):可以执行用于网络诊断等功能的命令.包括ping.tracert.telnet等命令,执行该级别命令的结果不能被保存到配置文件中. LEVEL 1(监控级):可以执行用于系统维 ...

  8. html js div随鼠标移动

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. lxd&openstack-lxd源码剖析

    lxd:https://linuxcontainers.org/lxd/,目标是融入到openstack体系被管理,像虚拟机一样被管理使用.从如下图可知,并非走的是libvirt-lxc路线,而是no ...

  10. spring第一章

    spring第一章 一.概述 Spring是一个开源框架,它由Rod Johnson创建.它是为了解决企业应用开发的复杂性而创建的.Spring使用基本的JavaBean来完成以前只可能由EJB完成的 ...