BZOJ.1061.[NOI2008]志愿者招募(线性规划 对偶原理 单纯形 / 费用流SPFA)
线性规划
用\(A_{ij}=0/1\)表示第\(i\)天\(j\)类志愿者能否被招募,\(x_i\)为\(i\)类志愿者招募了多少人,\(need_i\)表示第\(i\)天需要多少人,\(C_i\)表示\(i\)类招募志愿者的花费。
那么我们需要$$最小化\ Cx\s.t.\ Ax\geq need\x\geq 0$$
(s.t.:subject to,使得满足)
这是一个最小化线性规划,而不是标准型的最大化线性规划。根据对偶原理(见这儿),我们把它变成:$$最大化\ x*need\s.t.\ xA\leq C\x\geq 0$$
用非矩阵形式直观地写:$$最小化\ \sum_{i=1}mC_ix_i\s.t. \sum_{i=1}nA_{ij}x_j\geq need_i\ ,\ j=1,2,\ldots,m\x_j\geq 0$$
利用对偶原理,转化为:$$最大化\ \sum_{i=1}nneed_ix_i\s.t. \sum_{i=1}nA_{ij}x_i\leq C_j\ ,\ j=1,2,\ldots,m\x_i\geq 0$$
这样就可以直接用单纯形做了。因为\(C_i\)非负,所以一定有解,不需要Init()。
还有一个问题,线性规划的解是否可能非整数?
我不知道为什么有这么个...常识定理?
线性规划的问题的最优解为整数的一个必要条件是它的任意一个子方阵的行列式为\(-1, 0, 1\)。
反正这有\(Candy?\)的结论,我就记住吧。。
下面有洛谷上题解的证明,证明本题方阵的任意一个子方阵的行列式为\(-1, 0或1\)。(全单位模矩阵)
另外加强版,单纯形应该不对,然而数据比较水。
Proof:
//79668kb 1172ms(果然还是比费用流慢多了)
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define eps 1e-8
const int N=1005,M=10005;
int n,m;
double A[M][N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void Pivot(int r,int c)
{
double t=A[r][c]; A[r][c]=1;
for(int i=0; i<=n; ++i) A[r][i]/=t;
for(int i=0; i<=m; ++i)
if(i!=r && fabs(A[i][c])>eps)
{
double t=A[i][c]; A[i][c]=0;
for(int j=0; j<=n; ++j) A[i][j]-=t*A[r][j];
}
}
void Simplex()
{
for(int r,c; ; )
{
r=c=0;
for(int i=1; i<=n; ++i)
if(A[0][i]>eps) {c=i; break;}
if(!c) break;
double mn=1e15;
for(int i=1; i<=m; ++i)
if(A[i][c]>eps && mn>A[i][0]/A[i][c]) r=i, mn=A[i][0]/A[i][c];
if(!r) break;
Pivot(r,c);
}
}
int main()
{
n=read(), m=read();
for(int i=1; i<=n; ++i) A[0][i]=read();
for(int i=1,l,r; i<=m; ++i)
{
l=read(), r=read(), A[i][0]=read();
for(int j=l; j<=r; ++j) A[i][j]=1;
}
Simplex(), printf("%.0lf\n",-A[0][0]);
return 0;
}
费用流
用\(u\rightarrow v\ (f,\ c)\)表示一条\(u\rightarrow v\)容量为\(f\),花费为\(c\)的边。
对于每一类志愿者,连边\(l_i\rightarrow r_i+1\ (INF,\ cost_i)\);
每相邻的两天,连边\(i\rightarrow i+1\ (INF-need_i,\ 0)\);
对于源点汇点,连边\(S\rightarrow 1\ (INF,\ 0)\)、\(n+1\rightarrow T\ (INF,\ 0)\)。
因为一定存在可行解,所以最后流量一定可以扩充成INF。
对于每两天之间的连边会优先流,因为花费为0,而不经过这条边但覆盖这一段的边的流量之和一定不小于\(need_i\)。即缺少的流量会通过带权边补成INF,且能保证方案最优。
嗯...好吧我不会写费用流了。。
//1592kb 164ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1007,M=11007<<1,INF=0x3f3f3f3f;
int src,des,n,m,Enum,H[N],nxt[M],fr[M],to[M],cap[M],cost[M],pre[N],dis[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AddEdge(int u,int v,int w,int c)
{
fr[++Enum]=u, to[Enum]=v, nxt[Enum]=H[u], H[u]=Enum, cap[Enum]=w, cost[Enum]=c;
fr[++Enum]=v, to[Enum]=u, nxt[Enum]=H[v], H[v]=Enum, cap[Enum]=0, cost[Enum]=-c;
}
bool SPFA()
{
static std::queue<int> q;
static bool inq[N];
memset(dis,0x3f,sizeof dis);
dis[src]=0, q.push(src);
while(!q.empty())
{
int x=q.front(); q.pop();
inq[x]=0;
for(int v,i=H[x]; i; i=nxt[i])
if(cap[i] && dis[v=to[i]]>dis[x]+cost[i])
{
dis[v]=dis[x]+cost[i], pre[v]=i;
if(!inq[v]) inq[v]=1, q.push(v);
}
}
return dis[des]<INF;
}
int MCMF()
{
int res=0, mn=INF;
for(int i=des; i!=src; i=fr[pre[i]])
mn=std::min(mn,cap[pre[i]]);
for(int i=des,v=pre[i]; i!=src; i=fr[v],v=pre[i])
res+=mn*cost[v], cap[v]-=mn, cap[v^1]+=mn;
return res;
}
int main()
{
n=read(), m=read(), Enum=1, src=1, des=n+2;
for(int i=1; i<=n; ++i) AddEdge(i,i+1,INF-read(),0);
for(int i=1,l,r; i<=m; ++i) l=read(),r=read(),AddEdge(l,r+1,INF,read());
AddEdge(n+1,des,INF,0);
int res=0;
while(SPFA()) res+=MCMF();
printf("%d\n",res);
return 0;
}
BZOJ.1061.[NOI2008]志愿者招募(线性规划 对偶原理 单纯形 / 费用流SPFA)的更多相关文章
- BZOJ 1061: [Noi2008]志愿者招募【单纯形裸题】
1061: [Noi2008]志愿者招募 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 4813 Solved: 2877[Submit][Stat ...
- BZOJ 1061: [Noi2008]志愿者招募 [单纯形法]【学习笔记】
1061: [Noi2008]志愿者招募 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 3975 Solved: 2421[Submit][Stat ...
- BZOJ 1061: [Noi2008]志愿者招募 [单纯形法]【学习笔记看另一篇吧】
1061: [Noi2008]志愿者招募 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 3975 Solved: 2421[Submit][Stat ...
- BZOJ 1061: [Noi2008]志愿者招募
1061: [Noi2008]志愿者招募 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 4064 Solved: 2476[Submit][Stat ...
- BZOJ 1061: [Noi2008]志愿者招募 费用流
1061: [Noi2008]志愿者招募 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=1061 Description 申奥成功后,布布 ...
- 【刷题】BZOJ 1061 [Noi2008]志愿者招募
Description 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完 ...
- BZOJ 1061: [Noi2008]志愿者招募(线性规划与网络流)
http://www.lydsy.com/JudgeOnline/problem.php?id=1061 题意: 思路: 直接放上大神的建模过程!!!(https://www.byvoid.com/z ...
- BZOJ 1061 [Noi2008]志愿者招募(费用流)
题目描述 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i ...
- bzoj 1061 [Noi2008]志愿者招募(数学模型,MCMF)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1061 [题意] 雇人满足每天至少需要的人数. [思路一] Byvoid的题解 clic ...
随机推荐
- redis支持的数据结构
redis数据库里面的每个键值对都是由对象组成的. 其中数据库键的值总是字符串对象. 数据库的值则可以是字符串对象(String),列表对象(list),哈希对象(Hash),集合对象(Set),有序 ...
- Git与GitHub学习笔记(三).gitignore文件忽略和删除本地以及远程文件
一.Git提供了文件忽略功能.当对工作区某个目录或者某些文件设置了忽略后,git将不会对它们进行追踪 HELP:如何在IntelliJ IDEA中使用.ignore插件忽略不必要提交的文件 问题:最近 ...
- Lua程序设计(二)面向对象概念介绍
----------------------------------------------------------- Lua面向对象3 local smartMan = { name = " ...
- 流媒体技术学习笔记之(八)海康、大华IpCamera RTSP地址和格式
海康: rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream 说明: username: 用户名 ...
- 均方根值(RMS)+ 均方根误差(RMSE)+标准差(Standard Deviation)
均方根值(RMS)+ 均方根误差(RMSE)+标准差(Standard Deviation) 1.均方根值(RMS)也称作为效值,它的计算方法是先平方.再平均.然后开方. 2.均方根误差,它是观测值 ...
- chrome 隐藏技能之 base64 图片转换
有时候我们要转换图片为base64,或者将base64转回图片,可能都需要找一些在线工具或者软件类型的工具才行.当然 chrome 也算是软件,但是好在做前端的都有 chrome.好了,来看下简单的例 ...
- PHP中GET和POST区别
1. get是从服务器上获取数据,post是向服务器传送数据.2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到.post是通过H ...
- HDU 4506 小明系列故事——师兄帮帮忙(二分快速幂)
题意:就是输入一个数组,这个数组在不断滚动,而且每滚动一次后都要乘以一个数,用公式来说就是a[i] = a[i-1] * k;然后最后一位的滚动到第一位去. 解题报告:因为题目中的k要乘很多次,达到了 ...
- 关于golang的defer的练习
golang的defer怎么说.大意就是在函数return后.函数关闭前.按照filo的顺序来执行的关键字 上代码: package main import ( "fmt" ) f ...
- Phantomjs 抓取、分析某个页面加载时浏览器发起的所有的子请求
var page = require('webpage').create(), system = require('system'), address; if (system.args.length ...