P5331 [SNOI2019]通信 题解
本来以为要优化建图,但直接用zkw费用流艹过去了。
思路
大概是一个一眼网络流。
首先考虑建图。
通用模型,首先进行拆点,建立超级源点和超级汇点。
下文拆出的两个点一个叫点,一个叫次点。
源点向每一个点连一个流量为一,费用为零的边。
要注意,题目中所说的为直接连接到控制中心或者连接到前面的某个哨站。
所以次点向汇点连得边费用为零,流量为一(开始没想明白,样例都过不了)。
同时,每个点向汇点连一条流量为一,费用为 \(w\) 的边。
做法
我们可以发现,边数极其的多,普通的MCMF跑不过去,这里介绍一种更快的费用流方法zkw费用流,也就是多路增广的费用流算法。
zkw费用流
与普通费用流算法不同的是,zkw费用流在进行SPFA时,需要进行分层。
为多路增广做准备。
dfs和zkw部分与普通的dinic相差无几,具体细节可以看代码。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf = 1e18;
int n , w , s , t , cnt = 1 , a[1010] , head[2020];
int maxflow , ans , dis[2020] , dep[2020] , vis[2020] , gap[2020];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
struct edge
{
int to , nxt , val , cost;
} e[8000010];
inline void add(int x , int y , int z , int u)
{
e[++cnt] = (edge){y , head[x] , z , u} , head[x] = cnt;
e[++cnt] = (edge){x , head[y] , 0 , -u} , head[y] = cnt;
}
inline bool spfa()
{
memset(vis , 0 , sizeof(vis));
memset(dep , 0 , sizeof(dep));
for(int i = 1;i <= 2 * n + 2;i++) dis[i] = inf;
dis[s] = 0 , dep[s] = 1 , vis[s] = 1;
deque<int> q; q.push_back(s);
while(q.empty() == 0)
{
int x = q.front();
q.pop_front() , vis[x] = 0;
for(int i = head[x];i;i = e[i].nxt)
{
int y = e[i].to;
if(dis[y] > dis[x] + e[i].cost && e[i].val > 0)
{
dis[y] = dis[x] + e[i].cost , dep[y] = dep[x] + 1;
if(vis[y] == 0)
{
vis[y] = 1;
if(q.empty() == 0 && dis[y] < dis[q.front()]) q.push_front(y);
else q.push_back(y);
}
}
}
}
return dis[t] != inf;
}
bool flag;
inline int dfs(int now , int flow)
{
if(now == t)
{
maxflow += flow , flag = 1;
return flow;
}
int used = 0;
for(int i = head[now];i;i = e[i].nxt)
{
int y = e[i].to;
if(dis[now] + e[i].cost == dis[y] && dep[now] + 1 == dep[y] && e[i].val > 0)
{
int sum = dfs(y , min(e[i].val , flow - used));
used += sum , e[i].val -= sum , e[i ^ 1].val += sum , ans += sum * e[i].cost;
if(used == flow) return used;
}
}
return used;
}
inline void zkw()
{
while(spfa())
{
flag = 1;
while(flag) flag = 0 , dfs(s , inf);
}
cout << ans;
}
signed main()
{
n = read() , w = read() , s = 2 * n + 1 , t = 2 * n + 2;
for(int i = 1;i <= n;i++) a[i] = read();
for(int i = 1;i <= n;i++) add(s , i , 1 , 0),
for(int i = 1;i <= n;i++) add(i , t , 1 , w) , add(n + i , t , 1 , 0);
for(int i = 1;i <= n;i++)
for(int j = i + 1;j <= n;j++)
if(abs(a[i] - a[j]) < w) add(j , n + i , 1 , abs(a[i] - a[j]));
zkw();
return 0;
}
完结撒花!