Amber在论文中所提到的最大权闭合子图问题,跟上一题类似。
建图:对于每个中转站k,成本为w,连边(k,t,w);对于每个用户群g,盈利为w,连边(s,g,w),然后对用户群盈利所需的中转站a,b连边(g,a,∞),(g,b,∞),ans = ∑盈利 - maxflow
至于证明的话,比较复杂,可以见胡伯涛《最小割模型在信息学竞赛中的应用》
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 50000 + 10;
const int maxpoint = maxn << 1;
struct Edge
{
int pos,c;
int next;
}E[maxn << 3];
int head[maxpoint];
int dis[maxpoint],gap[maxpoint],cur[maxpoint],pre[maxpoint];
int s,t,nodenum,NE = 0;
int n,m,sum = 0;
void init()
{
freopen("bzoj1497.in","r",stdin);
freopen("bzoj1497.out","w",stdout);
}
inline void checkmin(int &a,int b)
{
if(a == -1 || a > b)a = b;
}
void insert(int u,int v,int c)
{
E[NE].c = c;E[NE].pos = v;
E[NE].next = head[u];head[u] = NE++;
E[NE].c = 0;E[NE].pos = u;
E[NE].next = head[v];head[v] = NE++;
}
void readdata()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
s = 0,t = n + m + 1;
nodenum = t + 1;
for(int i = 1;i <= n;i++)
{
int tmp;
scanf("%d",&tmp);
insert(i,t,tmp);
}
for(int i = 1;i <= m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
sum += c;
insert(s,i + n,c);
insert(i + n,a,inf);
insert(i + n,b,inf);
}
}
int sap()
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
for(int i = s;i <= t;i++)cur[i] = head[i];
int u = pre[s] = s,maxflow = 0,aug = -1;
gap[s] = nodenum;
while(dis[s] < nodenum)
{
loop: for(int &i = cur[u];i != -1;i = E[i].next)
{
int v = E[i].pos;
if(E[i].c && dis[u] == dis[v] + 1)
{
checkmin(aug,E[i].c);
pre[v] = u;
u = v;
if(v == t)
{
maxflow += aug;
for(u = pre[u];v != s;v = u,u = pre[u])
{
E[cur[u]].c -= aug;
E[cur[u]^1].c += aug;
}
aug = -1;
}
goto loop;
}
}
int mind = nodenum;
for(int i = head[u];i != -1;i = E[i].next)
{
int v = E[i].pos;
if(E[i].c && (mind > dis[v]))
{
cur[u] = i;
mind = dis[v];
}
}
if((--gap[dis[u]]) == 0)break;
gap[dis[u] = mind + 1]++;
u = pre[u];
}
return maxflow;
}
void solve()
{
int ans = sum - sap();
printf("%d\n",ans);
}
int main()
{
init();
readdata();
solve();
return 0;
}