题解 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
  1. 对于第 \(i\) 个中转站建立编号为 \(i\) 的点,对于第 \(i\) 个用户群建立编号为 \(i+n\) 的点。
  2. 连接 \(S\)\(1\sim n\) 的边,边权为 \(P_i\)
  3. 连接 \((n+1)\sim (n+m)\)\(T\) 的边,边权为 \(C_i\)
  4. 需要建立的最小割模型为:对于每个用户群,在损失的用户群利益和修建中转站的成本中二选一。所以对于每个用户群,连接 \(A_i\)\(B_i\) 到其边权为 \(inf\) 的边,这样就可以建立上面所述的最小割模型。

image

上面的 \(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;
}
posted @ 2023-03-08 09:12  芷陌陌吖  阅读(16)  评论(0)    收藏  举报