例题 BZOJ 3876
题意,有N个点,并且有一些边,每个点必须要经过一次,但是不限经过次数,也就是有下界的情况,且下界为1,现在每次的起点都是1,问至少花费的代价,使得每次从1开始,终点不限的情况下,走完所有的边(这里所谓的支线指的是每条边,点都被称之为剧情点)。
于是,很好发现是在说明一个有下限、无上限的最小费用可行流的这么一个问题了。
最小费用可行流的模型
给定一个网络,一个加权的有向图 G ,其中的每条边都有一个容量上界 C 。其中的两点: S 只有出度没有入度, T 只有入度没有出度。求 S 到 T 最大可以流过的流量,这是最大流的模型。
且满足以下条件:
- 容量限制:每条边的流量
- 流量平衡:任意一个点i,流入等于流出
那么,f是G的一个可行流,最大流即满足容量限制和流量平衡的最大的流。
如果在网络中,每条边增加一个流量下界B,这就是有上下界限制的网络流的模型了。
那么有上下界限制的网络流也是满足两个条件:
- 容量限制:每条边的流量
- 流量平衡:任意一个点i,流入等于流出
今天,我们要解决的这道题,就是一个有源汇有下界的最小费用可行流的这样一个问题。(其中比较特殊的是任意非1号结点都可以当作是汇点)
但是,首先,我们要先讲讲无源汇有上下限的最大流问题。
顾名思义,无源汇上下界可行流:没有源点 S ,汇点 T 。在网络中求可行流或者指出不存在。
对于这个问题,不好处理,但是如果我们去掉流量下界限制 B ,那么就是最大流的模型了,问题就可以解决了。
但是,我们不能直接去掉,因为有可能存在入!=出的情况。也就是说,当我们如果直接减去的话,会使得网络流的流量不守恒了。
那么,我们则需要加上附加流使得它达到一个平衡了,加多少呢?也就是每个点的出入流之间的差距取正来决定的。假设附加流是g,那么最后的实际流量就是B+g了。
此时我们去掉了流量下界限制 B ,那么网络中每条边的容量上界限也要减去,已经流过了 B 的流量,即新网络图中每条边的流量上界限制为 C - B ,下界限制0。
于是问题便成了求解最大流的问题了。
那么再看到有源汇的有上下限的最大流问题。
有源汇上下界可行流相比有源汇上下界可行流,多了源点 S 和汇点 T ,求从 S 到 T 满足每条边的流量都满足限制,且除S、T ,其他点都满足流量平衡。因为只有 S 和 T 不满足流量平衡,所以,如果可以使 S、T 也满足流量平衡,那么就可以直接套用无源汇上下界可行流了。
这里,我们定义超级源点和超级汇点。
源点的性质是只有流出的没有流入的,汇点恰好相反,而且对于源点流出的和汇点流入的,这些流量是相等的。所以建一条从 T 到 S 的边容量为INF,那么流入汇点T的流量就会从这条边流入 S 。有源汇到无源汇转换完成,跑一遍从 到 的最大流即可。可行流的流量也就是这条边的流量。
回到之前的问题上去。
我们现在要求解的是有源汇的有下限的最小费用可行流,有了上述的知识点,我们构造边,再跑一个最小费用流即可。
那么,每个点都是要经过至少一次,对于所有的出入流还要做到流量守恒,所以根据下限,我们可以确定网络流的最少流入,根据最少流入,我们就知道每个点的流入减去流出的值的情况,于是,流入多于流出的,我们让它和超级源点相链接,流入少于流出的我们让它和超级汇点相连接,并且流量就是|流入 - 流出|。
剩下的,我们就是去跑一个最大流最小费用的算法了。最后统计答案的时候别忘了加上基础的每条边的权值。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 310, maxM = 1e4 + 1210;
int N, M;
ll ans, In_det_Out[maxN] = {0};
struct Graph
{
int head[maxN], cnt;
struct Eddge
{
int nex, u, v; ll flow, cost;
Eddge(int a=0, int b=0, int c=0, ll d=0, ll f=0):nex(a), u(b), v(c), flow(d), cost(f) {}
} edge[maxM];
inline void addEddge(int u, int v, ll f, ll w)
{
edge[cnt] = Eddge(head[u], u, v, f, w);
head[u] = cnt++;
}
inline void _add(int u, int v, ll f, ll w) { addEddge(u, v, f, w); addEddge(v, u, 0, -w); }
inline void init()
{
cnt = 0;
for(int i=0; i<=N + 1; i++) head[i] = -1;
}
} Old;
struct MaxFlow_MinCost
{
int pre[maxN], S, T; ll Flow[maxN], dist[maxN];
queue<int> Q;
bool inque[maxN];
inline bool spfa()
{
for(int i=S; i<=T; i++) { pre[i] = -1; dist[i] = INF; inque[i] = false; }
while(!Q.empty()) Q.pop();
Q.push(S); dist[S] = 0; inque[S] = true; Flow[S] = INF;
while(!Q.empty())
{
int u = Q.front(); Q.pop(); inque[u] = false;
ll f, w;
for(int i=Old.head[u], v; ~i; i=Old.edge[i].nex)
{
v = Old.edge[i].v; f = Old.edge[i].flow; w = Old.edge[i].cost;
if(f && dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
Flow[v] = min(Flow[u], f);
pre[v] = i;
if(!inque[v])
{
inque[v] = true;
Q.push(v);
}
}
}
}
return ~pre[T];
}
inline ll EK()
{
ll sum_Cost = 0;
while(spfa())
{
int now = T, las = pre[now];
while(now ^ S)
{
Old.edge[las].flow -= Flow[T];
Old.edge[las ^ 1].flow += Flow[T];
now = Old.edge[las].u;
las = pre[now];
}
sum_Cost += dist[T] * Flow[T];
}
return sum_Cost;
}
inline void init()
{
S = 0; T = N + 1;
}
} MF;
inline void init()
{
ans = 0;
Old.init(); MF.init();
}
int main()
{
scanf("%d", &N);
init();
for(int i=1, ki, Bi, Ti; i<=N; i++)
{
scanf("%d", &ki); In_det_Out[i] -= ki;
while(ki --)
{
scanf("%d%d", &Bi, &Ti); ans += Ti; //下界至少为1,所以要先放入下界
In_det_Out[Bi]++;
Old._add(i, Bi, INF, Ti);
}
}
for(int i=1; i<=N; i++)
{
if(In_det_Out[i] > 0) //in > out
{
Old._add(MF.S, i, In_det_Out[i], 0);
}
else if(In_det_Out[i] < 0) //in < out
{
Old._add(i, MF.T, -In_det_Out[i], 0);
}
}
for(int i=2; i<=N; i++) Old._add(i, 1, INF, 0);
printf("%lld\n", ans + MF.EK());
return 0;
}
/*
6
2 2 1 3 2
2 4 3 5 4
2 5 5 6 6
0
0
0
*/