[NOI2008] 志愿者招募(线性规划-对偶问题-费用流)

problem

luogu-P3980

solution

志愿者连续工作 [si,ti][s_i,t_i][si,ti] 天,我们可以提炼出网络流二十四题中《最长k可重区间集问题》的模型。

同样地,把 1∼n1\sim n1n 天抽象成一条 1∼n+11\sim n+11n+1 个点的链条。

  • 源点 s→1s\rightarrow 1s1 容量无穷费用零,n+1→tn+1\rightarrow tn+1t 汇点 容量无穷费用零。
  • 然后 i→i+1i\rightarrow i+1ii+1 容量 ∞−ai\infty-a_iai 费用零。
  • 对于第 iii 种志愿者,si→ti+1s_i\rightarrow t_i+1siti+1 容量无穷费用 cic_ici

最后跑最小费用最大流即为答案。

如果无法直接理解这样建图的正确性,可以考虑把网络图中的流量流起来。

如果从 sss 沿着费用零的边向 ttt 流,由于链条上的边流量为 ∞−ai\infty-a_iai,所以先前 ∞\infty 的不能流完。

那么势必要通过有志愿者(花费)的边流。

假设流到了点 iii,那么剩下不能流过 (i,i+1)(i,i+1)(i,i+1) 的流量我们得从 iii 连出去的志愿者边流,并且流一个就要花费 cic_ici 的代价。

然后在点 x+1x+1x+1 的时候这些流量又会汇合。

这就相当于招募了从 iii 开始到 xxx 结束的志愿者。(当然可能有多个 xxx 结束点)

反正到最后 ttt 的时候,流量总和一定会汇聚成从 sss 开始流的 ∞\infty

你可以理解一队人去闯密室逃脱,在一定关卡要进行多人支线任务,需要大部队派一些人去完成,然后主线队继续往下走主线任务,到了一定关卡有些人完成了自己的支线任务可以归队了。最后通关的时候,一定是大家都从主线任务关卡口出来。

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000
#define maxm 50000
#define int long long
#define inf 0x3f3f3f3f
struct node { int to, nxt, flow, cost; }E[maxm];
int head[maxn], dis[maxn], lst[maxn], vis[maxn], a[maxn];
int cnt = -1, n, m, s, t;
queue < int > q;void addedge( int u, int v, int w, int c ) {E[++ cnt] = { v, head[u], w, c }; head[u] = cnt;E[++ cnt] = { u, head[v], 0,-c }, head[v] = cnt;
}bool SPFA() {memset( lst, -1, sizeof( lst ) );memset( dis, 0x3f, sizeof( dis ) );q.push( dis[s] = 0 );while( ! q.empty() ) {int u = q.front(); q.pop(); vis[u] = 0;for( int i = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( dis[v] > dis[u] + E[i].cost and E[i].flow ) {dis[v] = dis[u] + E[i].cost; lst[v] = i;if( ! vis[v] ) vis[v] = 1, q.push( v );}}}return ~ lst[t];
}int MCMF() {int ans = 0;while( SPFA() ) {int flow = inf;for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )flow = min( flow, E[i].flow );for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) {E[i ^ 1].flow += flow;E[i].flow -= flow;ans += flow * E[i].cost;}}return ans;
}signed main() {memset( head, -1, sizeof( head ) );scanf( "%lld %lld", &n, &m );s = 0, t = n + 2;for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );for( int i = 1;i <= n;i ++ ) addedge( i, i + 1, inf - a[i], 0 );addedge( s, 1, inf, 0 );addedge( n + 1, t, inf, 0 );for( int i = 1, u, v, w;i <= m;i ++ ) {scanf( "%lld %lld %lld", &u, &v, &w );addedge( u, v + 1, inf, w );}printf( "%lld\n", MCMF() );return 0;
}

solution(流量平衡)

假设共 333 天,第 iii 天招募 pip_ipi 人。

共有三类志愿者:

  • 从第 111 天工作到第 333 天,费用为 c1c_1c1,招募了 b1b_1b1 人。
  • 从第 222 天工作到第 333 天,费用为 c2c_2c2,招募了 b2b_2b2 人。
  • 从第 111 天工作到第 222 天,费用为 c3c_3c3,招募了 b3b_3b3 人。

则有以下不等式:
{b1+b3≥a1b1+b2+b3≥a2b1+b2≥a3\begin{cases} b_1+b_3\ge a_1\\b_1+b_2+b_3\ge a_2\\b_1+b_2\ge a_3 \end{cases} b1+b3a1b1+b2+b3a2b1+b2a3
记第 iii 天招募的志愿者超出最少要求人数 did_idi 人,显然 di≥0d_i\ge 0di0。则可改写成以下等式:
{p1=b1+b3=a1+d1p2=b1+b2+b3=a2+d2p3=b1+b2=a3+d3\begin{cases}p_1=b_1+b_3=a_1+d_1\\p_2=b_1+b_2+b_3=a_2+d_2\\p_3=b_1+b_2=a_3+d_3\end{cases} p1=b1+b3=a1+d1p2=b1+b2+b3=a2+d2p3=b1+b2=a3+d3
将相邻两两等式作差后移项整理得:
{p1=b1+b3=a1+d1p2−p1=b2−b3=a2−a1+d2−d1p3−p2=−b3=a3−a2+d3−d2−p3=−b1−b2=−a3−d3⇒{p1−p0=b1+b3−a1−d1=0p2−p1=b2−b3−a2+a1−d2+d1=0p3−p2=−b3−a3+a2−d3+d2=0p4−p3=−b1−b2+a3+d3=0\begin{cases}p_1=b_1+b_3=a_1+d_1\\p_2-p_1=b_2-b_3=a_2-a_1+d_2-d_1\\ p_3-p_2=-b_3=a_3-a_2+d_3-d_2\\-p_3=-b_1-b_2=-a_3-d_3\end{cases}\Rightarrow \begin{cases}p_1-p_0=b_1+b_3-a_1-d_1=0\\p_2-p_1=b_2-b_3-a_2+a_1-d_2+d_1=0\\ p_3-p_2=-b_3-a_3+a_2-d_3+d_2=0\\p_4-p_3=-b_1-b_2+a_3+d_3=0 \end{cases} p1=b1+b3=a1+d1p2p1=b2b3=a2a1+d2d1p3p2=b3=a3a2+d3d2p3=b1b2=a3d3p1p0=b1+b3a1d1=0p2p1=b2b3a2+a1d2+d1=0p3p2=b3a3+a2d3+d2=0p4p3=b1b2+a3+d3=0
网络流中除了源汇点,其余点都应满足流量平衡,即流入流量等于流出流量;若将流入记为正,流出记为负,则应满足流入流出流量的代数和为 000

网络图中一条连接 x,yx,yx,y 的边,在 x,yx,yx,y 的流量平衡等式中各出现一次,且一次为正一次为负。

所以我们可以对上面最后化出的等式每个建立一个点,这个等式表示的就是这个点流量平衡。

再观察最后的等式:

observationⅠ.\text{observationⅠ.}observationⅠ. bi,dib_i,d_ibi,di 都在恰好两个等式出现,且是一正一负。所以每一个变量 bi,dib_i,d_ibi,di 都可以作为网络图中的一条边。

observationⅡ.\text{observationⅡ.}observationⅡ. 常量 aia_iai 也恰好在两个等式中出现,且是一正一负。为正时表示流入,可以和源点连边;为负时表示流出,可以和汇点连边。

根据作差规则,aia_iai 一定是出现在第 i,i+1i,i+1i,i+1 两个等式中,且一定第 iii 个等式为负,第 i+1i+1i+1 个为正。

常量与源汇点连边,变量表示常量点之间的边跑平衡。

最后答案是 min⁡∑bi⋅ci\min \sum b_i·c_iminbici ,可以以“费用”的形式表示出来。

  • 简述建图方式:

    假设 a0=an+1=0a_0=a_{n+1}=0a0=an+1=0

    • 建立源汇点 s,ts,ts,t
    • 建立点 1∼n+11\sim n+11n+1,代表 n+1n+1n+1 个等式。
    • i+1i+1i+1 个点向第 iii 个点连一条容量无穷,费用为零的边。对应 bi,dib_i,d_ibi,di 的平衡。
    • iii 类志愿者连边 si→ti+1s_i\rightarrow t_i+1siti+1,容量无穷,费用为 cic_ici
    • 对于第 iii 个点,若 ai−ai−1a_i-a_{i-1}aiai1 为正,连边 s→is\rightarrow isi,容量 ai−ai−1a_i-a_{i-1}aiai1 费用为零;若为负,连边 i→ti\rightarrow tit,容量 ai−1−aia_{i-1}-a_iai1ai 费用为零。相当于等式中的常数项。

实际理解上可以把常数项提到右边:
{b1+b3−d1=a1−a0b2−b3−d2+d1=a2−a1−b3−d3+d2=a3−a2−b1−b2+d3=a4−a3\begin{cases} b_1+b_3-d_1=a_1-a_0\\ b_2-b_3-d_2+d_1=a_2-a_1\\ -b_3-d_3+d_2=a_3-a_2\\ -b_1-b_2+d_3=a_4-a_3 \end{cases} b1+b3d1=a1a0b2b3d2+d1=a2a1b3d3+d2=a3a2b1b2+d3=a4a3
ai−ai−1a_i-a_{i-1}aiai1 当成第 iii 个等式的盈亏量,这样你就能理解正负与源汇连边的意义了。

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000
#define maxm 50000
#define int long long
#define inf 0x3f3f3f3f
struct node { int to, nxt, flow, cost; }E[maxm];
int head[maxn], dis[maxn], lst[maxn], vis[maxn], a[maxn];
int cnt = -1, n, m, s, t;
queue < int > q;void addedge( int u, int v, int w, int c ) {E[++ cnt] = { v, head[u], w, c }; head[u] = cnt;E[++ cnt] = { u, head[v], 0,-c }, head[v] = cnt;
}bool SPFA() {memset( lst, -1, sizeof( lst ) );memset( dis, 0x3f, sizeof( dis ) );q.push( dis[s] = 0 );while( ! q.empty() ) {int u = q.front(); q.pop(); vis[u] = 0;for( int i = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( dis[v] > dis[u] + E[i].cost and E[i].flow ) {dis[v] = dis[u] + E[i].cost; lst[v] = i;if( ! vis[v] ) vis[v] = 1, q.push( v );}}}return ~ lst[t];
}int MCMF() {int ans = 0;while( SPFA() ) {int flow = inf;for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )flow = min( flow, E[i].flow );for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) {E[i ^ 1].flow += flow;E[i].flow -= flow;ans += flow * E[i].cost;}}return ans;
}signed main() {memset( head, -1, sizeof( head ) );scanf( "%lld %lld", &n, &m );s = 0, t = n + 2;for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );for( int i = 1;i <= n;i ++ ) addedge( i + 1, i, inf, 0 );for( int i = 1, u, v, w;i <= m;i ++ ) {scanf( "%lld %lld %lld", &u, &v, &w );addedge( u, v + 1, inf, w );}for( int i = 1;i <= n + 1;i ++ )if( a[i] - a[i - 1] > 0 ) addedge( s, i, a[i] - a[i - 1], 0 );else addedge( i, t, a[i - 1] - a[i], 0 );printf( "%lld\n", MCMF() );return 0;
}

其实流量平衡的建边含义理解还有从线性规划对偶角度出发的。会在《防守战线》中详细说明。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316451.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

互达的集合(线段树)

problem 给定数组 l,rl,rl,r。求有多少个非空集合 SSS&#xff0c;满足 ∀i,j∈Sli≤j≤ri\forall_{i,j\in S}\ l_i\le j\le r_i∀i,j∈S​ li​≤j≤ri​。 集合内对于任意一个点而言&#xff0c;其余点均能被自己的范围覆盖到。 n≤2e5n\le 2e5n≤2e5。 solution 分享一下…

【学习笔记】线性规划与对偶问题和LP对偶费用流([ZJOI2013]防守战线题解)

线性规划与对偶问题 原问题&#xff1a; min⁡{7x1x25x3}s.t.{x1−x23x3≥105x12x2−x3≥6xi≥0\min\{7x_1x_25x_3\} \\ s.t.\begin{cases} x_1-x_23x_3\ge 10\\ 5x_12x_2-x_3\ge 6\\ x_i\ge 0\end{cases} min{7x1​x2​5x3​}s.t.⎩⎪⎨⎪⎧​x1​−x2​3x3​≥105x1​2x2​−…

【学习笔记】DAG / 一般有向图的支配树 / 灭绝树

定义与声明 一个有向图 GGG。给定一个起点 sss&#xff0c;假设 sss 能到达所有点。 若去掉某个点 iii 后&#xff0c;sss 无法到达 jjj&#xff0c;则称 iii 为 jjj 的支配点。 显然支配点存在传递关系。 以 sss 为根&#xff0c;使得对于任意节点 iii&#xff0c;其树上祖…

[ZJOI2014] 星系调查(树上差分 + 数学推式子)

problem luogu-P3340 题面写得那么长&#xff0c;其实说白了就是求一条直线&#xff0c;使得若干个点到这条直线的距离平方的和最小&#xff0c;求这个最小值。 solution 我超爱数学&#xff0c;数学就是我的命&#xff0c;我一天不学数学我就难受&#xff01; 假设拟合出…

[ZJOI2014] 璀璨光华(bfs建图 + dfs搜索)

problem luogu-P3342 solution 你感觉这道题没考什么&#xff0c;又感觉考了什么 通过样例以及题面&#xff0c;我们并未获取到『立方体每个小方块的编号是按一定规则命名』的信息。 也就是说&#xff0c;我们需要通过输入的每个小方块相邻的编号的信息来建出这个立方体的…

cf1523A. Game of Life

cf1523A. Game of Life 题意&#xff1a; 包含n个元素的数组&#xff0c;数值为1或0&#xff0c;如果一个元素为0&#xff0c;并且其周围正好只有一个为1的元素&#xff0c;那么下一刻本元素也会变成1. 给你一个数值&#xff0c;问你m次时刻后数组的状态 题解&#xff1a; …

SignalR 中丰富多彩的消息推送方式

在上一篇 SignalR 文章中&#xff0c;演示了如何通过 SignalR 实现了简单的聊天室功能&#xff1b;本着简洁就是美的原则&#xff0c;这一篇我们也来聊聊在 SignalR 中的用户和组的概念&#xff0c;理解这些基础知识有助于更好的开发基于 SignalR 的应用&#xff0c;通过对用户…

Visual Studio 2019 使用 Live Share

一.前言Visual Studio 2019 在今天发布&#xff08;北京时间&#xff09;了&#xff0c;这次带来了一个比较有趣的 Live Share 功能&#xff0c;使用它可以进行更好的协作开发。主要功能&#xff1a;更多资料可看官方介绍&#xff1a;Visual Studio 实时共享什么是Visual Studi…

cf1523B. Lord of the Values

cf1523B. Lord of the Values 题意&#xff1a; 给你一个数组&#xff0c;有n个数&#xff0c;n为偶数&#xff0c;a1&#xff0c;a2…an 现在有两个操作&#xff1a; 对于i<j 操作1&#xff1a;aiaiaj 操作2&#xff1a;ajaj-ai 把原数组转换为-a1,-a2,-a3… 题解&#…

Asp.Net Core WebAPI使用Swagger时API隐藏与分组

1、前言为什么我们要隐藏部分接口&#xff1f;因为我们在用swagger代替接口的时候&#xff0c;难免有些接口会直观的暴露出来&#xff0c;比如我们结合Consul一起使用的时候&#xff0c;会将健康检查接口以及报警通知接口暴露出来&#xff0c;这些接口有时候会出于方便考虑&…

cf1526E. Oolimry and Suffix Array(未解决)

E. Oolimry and Suffix Array 题意&#xff1a; 给定n&#xff0c;k和长度为n的后缀数组si(0<si<n-1),求长度为n的由k种字母构成的字符串种&#xff0c;后缀数组为si的有多少种 题解&#xff1a; 题解看懂了一半。。。等会了再更新 代码&#xff1a;

P2572 [SCOI2010]序列操作

P2572 [SCOI2010]序列操作 题意&#xff1a; 一个长度为n的01序列&#xff0c;下标从0开始&#xff0c;现在有五种变换操作和询问操作&#xff1a; 0 l r 把[l,r]区间内的所有数全变成01 l r 把[l,r]区间内的所有数全变成12 l r把[l,]区间内所有数全部取反3 l r询问[l,r]区间…

为您的机器配置开发环境

软件准备在本期视频中&#xff0c;我们将安装所序言的软件&#xff0c;并为我们的电脑配置.NET Core开发环境。我们需要安装如下2个软件&#xff1a;一个编辑器.NET Core SDK&#xff08;Software Development Kit&#xff09;下载并安装.NET Core 开发编辑器我的是Windows操作…

[CQOI2012] 局部极小值(状压DP + 容斥 + 搜索)

problem luogu-P3160 solution 这么小的数据范围&#xff0c;非暴力不状压。暴力 O(28!)O(28!)O(28!) 呵呵呵可以拉走了。 我们不妨从小到大填数字&#xff0c;这样如果局部极小值点还没有填的话&#xff0c;周围的九宫格就一定不能被填。 设 dp(s,i):dp(s,i):dp(s,i): 局…

基于Kubernetes 构建.NET Core 的技术体系

很多公司技术支持岗位的工作&#xff0c;如配置域名&#xff0c;部署环境&#xff0c;修改复位配置&#xff0c;服务重启&#xff0c;扩容缩容&#xff0c;梳理和完善监控&#xff0c;根据开发的需要查找日志等工作&#xff0c;需要和开发进行大量的沟通&#xff0c;如什么是外…

P4062 [Code+#1]Yazid 的新生舞会(分治做法)

P4062 [Code#1]Yazid 的新生舞会 题意&#xff1a; 给出一个序列&#xff0c;求有多少个子区间满足众数的出现次数大于区间长度的一半。 出现次数大于区间长度的一般我们称之为绝对众数 题解&#xff1a; 分治做法 对于一个区间[l,r]&#xff0c;设mid⌊lr2⌋\lfloor \frac…

[CQOI2014] 危桥(网络流)

problem luogu-P3163 solution 这是一道网络流好题&#xff0c;看的着摸不着吃不着。 初读完题&#xff0c;就知道这是一道“脱光了”的最大流。 建图基础版本&#xff1a; 建立额外源汇点。 无向边相同于两条有向边&#xff0c;直接建不影响&#xff0c;危桥流量设成 22…

那些优秀的开发者----汪宇杰:从重视细节,到成就技术专家

汪宇杰&#xff08;Edi Wang&#xff09;&#xff0c;.NET及Windows开发者&#xff0c;2018-2019年度微软最有价值专家&#xff08;Windows Development方向&#xff09;。现担任某金融科技企业的网站开发工程师。他对微软技术有鉴定的信念和不灭的热情&#xff0c;曾在Windows…

P4062 [Code+#1]Yazid 的新生舞会(线段树做法)

P4062 [Code#1]Yazid 的新生舞会&#xff08;线段树做法&#xff09; 题意&#xff1a; 给你一个序列a[1…n]​&#xff0c;求存在绝对众数的子区间个数。 绝对众数指&#xff1a;区间中出现次数最多的那个数&#xff0c;出现次数严格大于区间长度的一半。 题解&#xff1a…

《从零开始学ASP.NET CORE MVC》课程介绍

大家好&#xff0c;欢迎来到52ABP学院&#xff0c;收看我们的 《从零开始学ASP.NET CORE MVC》。ASP.NET Core 简介从2015年开始随时互联网成长&#xff0c;云计算和AI、大数据的爆发&#xff0c;大家从谈论项目信息化到数字化的转型&#xff0c;从ToC产业到ToB产业的转型&…