bzoj1061-[Noi2008]志愿者招募-单纯形 & 费用流
有\(n\)天,\(m\)类志愿者,一个第\(i\)类志愿者可以从第\(s_i\)天工作到第\(t_i\)天,第\(i\)天工作的志愿者不少于\(b_i\)个。每一类志愿者都有单价\(c_i\),问满足要求的最小花费。
分析
它其实是这样一个问题:
AX\ge B \\
X\ge 0 \\
A_{ij}=\begin{cases}
1 && s_i\le j\le t_i \\
0 && \text{otherwise}
\end{cases}
\]
这是一个线性规划问题。
单纯形法
线性规划的一种通用解法是单纯形法,复杂度为指数级,但一般情况下(不去卡它)效率十分令人满意。
要使用单纯形法,我们需要把线性规划转化成一般形式:
AX\le B \\
X\ge 0
\]
然而这里的问题是,我们的线性规划是一个最小化问题,用的都是大于号,不是标准形式。这时候需要用强对偶定理转化这个问题。
如果一个线性规划有最优解,那么它的对偶问题也有最优解,并且最优解相同。
一个标准型的对偶问题如下:
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]志愿者招募-单纯形 & 费用流的更多相关文章
- [BZOJ1061][Noi2008]志愿者招募 线性规划+费用流
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1061 根据题意列方程,然后用网络流解线性规划. 题解直接贴ByVoid的吧,太神了:htt ...
- 【BZOJ】1061: [Noi2008]志愿者招募(费用流+数学)
http://www.lydsy.com/JudgeOnline/problem.php?id=1061 好神的一题! 学会了一种建模方式: 当方程组内的任意变量都在其中两个方程出现且一正一负,可以建 ...
- BZOJ 1061 [Noi2008]志愿者招募(费用流)
题目描述 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i ...
- NOI2008 志愿者招募 (费用流)
题面 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i 天至 ...
- [BZOJ1061][Noi2008]志愿者招募
[BZOJ1061][Noi2008]志愿者招募 试题描述 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难 题:为即将启动的奥运新项目招募一批短期志愿 ...
- 网络流解线性规划问题 BZOJ1061: [Noi2008]志愿者招募
线性规划定义: 在给定有限的资源和竞争约束情况下,很多问题都可以表述为最大化或最小化某个目标.如果可以把目标指定为某些变量的线性函数,而且如果可以将资源约束指定为这些变量的等式或不等式,则得到了一个线 ...
- 【费用流】BZOJ1061: [Noi2008]志愿者招募(这题超好)
1061: [Noi2008]志愿者招募 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 5291 Solved: 3173[Submit][Stat ...
- [BZOJ1061] [Noi2008] 志愿者招募 (费用流)
Description 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难 题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能 ...
- 线性规划费用流解法(Bzoj1061: [Noi2008]志愿者招募)
题面 传送门 Sol 线性规划费用流解法用与求解未知数为非负数的问题 这道题可以列出一堆形如 \(x[i]+x[j]+x[k]+...>=a[p]\) 的不等式 我们强行给每个式子减去一个东西, ...
随机推荐
- xgboost算法教程(两种使用方法)
标签: xgboost 作者:炼己者 ------ 欢迎大家访问我的简书以及我的博客 本博客所有内容以学习.研究和分享为主,如需转载,请联系本人,标明作者和出处,并且是非商业用途,谢谢! ------ ...
- 安装centos minimal 版本后的网络配置(linux)
1.修改网卡配置文件 2.重启网络服务 3.测试网络
- selenium+python 搭建自动化环境
一.以搭建windows平台为例 准备工具如下: 1)下载Python 2)安装,配置环境变量 3)安装selenium,通过pip安装,命令如下: pip install selenium 方式二 ...
- 合并SQL 调优
SELECT le.equipcode,sum(case when wo.ordertype=0 then 1 else 0 END) as wxcount,sum(case when wo.orde ...
- 3.5星|《哈佛商学院最受欢迎的领导课》:讲给CEO的管理学常识、常见错误和改进方法
哈佛商学院最受欢迎的领导课 英文版出版于2011年,还不算旧.中信2013年出过一版,这版估计是英文书版权过期后重新购买了再出版. 全书以写给CEO的口吻讲了许多管理常识,包含一些CEO容易犯的问题和 ...
- 【snmp】Linux开启snmp及查询
1.Linux snmp 1.安装snmp yum install -y net-snmp* 2.备份snmp配置 cp /etc/snmp/snmpd.conf /etc/snmp/snmpd.co ...
- CQOI2018 游记 再见OI,既是反思,也是祝福
哎,怎么说呢? 时运不齐,命途多舛? 从头开始说吧. 今年的NOIP大家考的都不尽人意,每个人都有或多或少的失误,全部都几十分几十分地丢.最后大家剩下的觉得可能冲击一下省队的人一共只有7个. 伙伴们变 ...
- php爬虫学习笔记1 PHP Simple HTML DOM Parser
常用爬虫. 0. Snoopy是什么? (下载snoopy) Snoopy是一个php类,用来模仿web浏览器的功能,它能完成获取网页内容和发送表单的任务. Snoopy的一些特点: * ...
- LeetCode 289. Game of Life (C++)
题目: According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a ce ...
- c# richBox内容转图片
1.自定义控件,继承richBox public class RichTextBoxPrintCtrl : RichTextBox { //private const double anInch = ...