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]\) 的不等式 我们强行给每个式子减去一个东西, ...
随机推荐
- 自己在UWP程序上调用usb转串口的路程
之前一直是在普通的framework环境下写串口,使用的类为 SerialPort 这个类大家可能比较熟悉.但是在UWP的开发里,使用的是 SerialDevice 这个类,是不一样的. 1. 清单文 ...
- NetWork——TCP的流量控制和拥塞控制
,然后开始执行拥塞避免算法,使拥塞窗口缓慢地线性增大. 这里为什么替换掉了慢开始算法呢,这是因为收到重复的ACK不仅仅告诉我们一个分组丢失了,由于接收方只有在收到另一个报文段时才会产生重复的ACK,所 ...
- KDTable如何添加合计行?
/** * 功能:添加合计行 * * @param table * 指定的KDTable * @param fields * 需要合计的列 */ public static void apendFoo ...
- Awesome TensorFlow
Awesome TensorFlow A curated list of awesome TensorFlow experiments, libraries, and projects. Inspi ...
- Android开源的精美日历控件,热插拔设计的万能自定义UI
Android开源的精美日历控件,热插拔设计的万能自定义UI UI框架应该逻辑与界面实现分离,该日历控件使用了热插拔的设计 ,简单几步即可实现你需要的UI效果,热插拔的思想是你提供你的实现,我提供我的 ...
- 随笔1-本想吐槽cnblog编辑器,但是今天猛然发现它竟然这么干净简洁
日常用写作编辑器 多少和我一样有选择综合征的? 不愿意写博客的其中一个非常重要的原因是编辑器不好用! 博客的迁移是相当麻烦的! 所以定下cnblog写东西也是一种勇气!鼓励.... FlashNote ...
- 622.设计循环队列 javascript实现
设计你的循环队列实现. 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环.它也被称为“环形缓冲器”. 循环队列的一个好处是我们可以利用这个队列 ...
- Zabbix自动发现之fping
原文发表于cu:2016-06-21 Zabbix自动发现功能从配置流程上比较简单:Discovery与Action. 在做Zabbix的自动发现验证时,使用"ICMP ping" ...
- No module named MYSQLdb 报错
问题描述: 报错:ImportError: No module named MySQLdb 对于不同的系统和程序有如下的解决方法: easy_install mysql-python (mix os) ...
- 利用xlsxwriter生成数据报表
#!/usr/bin/env python# -*- coding:utf-8 -*-import os,xlsxwriter,datetimeimport ConfigParserfrom send ...