题解 P4174
题意
有 \(n\) 个通讯信号中转站,第 \(i\) 个中转站的需要的成本为 \(P_i\)。
有 \(m\) 个用户群,第 \(i\) 个用户群会使用中转站 \(A\) 和 \(B\) 进行通讯,可以获利 \(C_i\)。
求最大净获利(净获利 = 获益之和 – 投入成本之和)。
数据范围:\(1\le n\le 5\times10^3\),\(1\le m\le 5\times10^4\),\(0\le C_i,P_i\le 100\)。
题解
考虑建立最大流模型,但由于 \(P_i\) 在答案中为负,无法建图,所以考虑把所有权都转化为正权。
考虑 \(all=\sum_{i=1}^m C_i\),那么净利润为 \(all\) 减去成本和损失的用户群利益。
我们要最小化减去的值,考虑使用最小割模型。
以下面的数据为例:
5 5
1 2 3 4 5
1 2 3
2 3 4
1 3 3
1 4 2
4 5 3
- 对于第 \(i\) 个中转站建立编号为 \(i\) 的点,对于第 \(i\) 个用户群建立编号为 \(i+n\) 的点。
- 连接 \(S\) 到 \(1\sim n\) 的边,边权为 \(P_i\)。
- 连接 \((n+1)\sim (n+m)\) 到 \(T\) 的边,边权为 \(C_i\)。
- 需要建立的最小割模型为:对于每个用户群,在损失的用户群利益和修建中转站的成本中二选一。所以对于每个用户群,连接 \(A_i\) 和 \(B_i\) 到其边权为 \(inf\) 的边,这样就可以建立上面所述的最小割模型。

上面的 \(inf\) 边的作用就是让冲突的边无法同时保留(以及连接作用)。
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=55005,M=310005,INF=0x7fffffff;
int n,m,s,t,maxn,all;
int now[N],d[N];
int head[N],nxt[M],edge[M],ver[M],tot=1;
inline int read()
{
int x=0,f=1;
char c=getchar();
while (c<'0'||c>'9')
{
if (c=='-')
{
f=-1;
}
c=getchar();
}
while (c>='0'&&c<='9')
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(int x)
{
if (x<0)
{
putchar('-');
x=-x;
}
if (x>9)
{
write(x/10);
}
putchar((x%10)^48);
return;
}
inline void add(int x,int y,int z)
{
edge[++tot]=z;
ver[tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
inline bool bfs()
{
memset(d,0,sizeof(d));
queue <int> q;
d[s]=1;
now[s]=head[s];
q.push(s);
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x];i;i=nxt[i])
{
if (edge[i]!=0&&d[ver[i]]==0)
{
d[ver[i]]=d[x]+1;
now[ver[i]]=head[ver[i]];
q.push(ver[i]);
if (ver[i]==t) return true;
}
}
}
return false;
}
inline int dinic(int x,int flow)
{
if (x==t)
{
return flow;
}
int rest=flow;
int i;
for (i=now[x];i;i=nxt[i])
{
if (edge[i]!=0&&d[ver[i]]==d[x]+1)
{
int k=dinic(ver[i],min(rest,edge[i]));
if (k==0)
{
d[ver[i]]=0;
continue;
}
edge[i]-=k;
edge[i^1]+=k;
rest-=k;
if (rest==0) break;
}
}
now[x]=i;
return flow-rest;
}
int main()
{
n=read();
m=read();
t=n+m+1;
for (int i=1;i<=n;i++)
{
int p=read();
add(0,i,p);
add(i,0,0); //步骤 1,2
}
for (int i=1;i<=m;i++)
{
int a=read();
int b=read();
int c=read();
add(a,i+n,INF);
add(i+n,a,0);
add(b,i+n,INF);
add(i+n,b,0);
add(i+n,t,c);
add(t,i+n,0); //步骤 1,3,4
all+=c;
}
int flow=0; //求最小割
while (bfs())
{
while (flow=dinic(s,INF))
{
maxn+=flow;
}
}
write(all-maxn);
return 0;
}

浙公网安备 33010602011771号