题目链接:
最大权闭合子图应用。
对于每一个中转站,向汇点连边,容量为费用的绝对值。
对于每一个用户,向源点连边,容量为收益。同时向他需要的中转站连边,容量为(infty)。
然后所有用户的收益-上图最小割即为所求答案。
为什么呢?
因为中转站与汇点连边若割断则代表建立中转站(答案减去费用)
若割断用户与源点连边则代表放弃用户(答案减去收益)
那么上网络流即可。
这里写了(Dinic)(忘判细节,(TLE*n),加了当前弧负优化常数x4)和(ISAP)两种方式。
(Dinicquad 6320kbquad 1012ms(BZOJ))
#include <queue>
#include <cstdio>
#include <cstring>
inline int Min(int a,int b){return a<b?a:b;}
int n,m,St,Ed;
int Head[55555],Next[400005],To[400005],Val[400005],En=1;
int Dep[55555],Cur[55555];
inline void Add(int x,int y,int z)
{
Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}
bool BFS()
{
std::queue<int> q;
memset(Dep,0,sizeof Dep);
q.push(St),Dep[St]=1;
for(int x,y;!q.empty();q.pop())
for(int i=Head[x=q.front()];i;i=Next[i])
if(Val[i]&&!Dep[y=To[i]])
{
Dep[y]=Dep[x]+1,q.push(y);
if(y==Ed)return true;
}
return false;
}
int Dinic(int x,int Flow)
{
if(x==Ed)return Flow;
int Rest=Flow;
for(int i=Head[x],y;i&&Rest;i=Next[i])
if(Val[i]&&Dep[y=To[i]]==Dep[x]+1)
{
int k=Dinic(y,Min(Val[i],Rest));
if(!k)Dep[y]=0;
else Val[i]-=k,Val[i^1]+=k,Rest-=k;
}
return Flow-Rest;
}
int main()
{
scanf("%d%d",&n,&m),St=n+m+1,Ed=St+1;
int Ans=0;
for(int i=1,p;i<=n;++i)
scanf("%d",&p),Add(i,Ed,p);
for(int i=1,a,b,c;i<=m;++i)
{
scanf("%d%d%d",&a,&b,&c);
Add(i+n,a,0x3f3f3f3f);
Add(i+n,b,0x3f3f3f3f);
Add(St,i+n,c),Ans+=c;
}
int MaxFlow=0,Fs;
while(BFS())
while((Fs=Dinic(St,0x3f3f3f3f)))
MaxFlow+=Fs;
printf("%d
",Ans-MaxFlow);
return 0;
}
(ISAPquad 6596kbquad 652ms(BZOJ))
#include <queue>
#include <cstdio>
#include <cstring>
inline int Min(int a,int b){return a<b?a:b;}
int n,m,St,Ed;
int Head[55555],Next[400005],To[400005],Val[400005],En=1;
int Dis[55555],Cnt[55555],Cur[55555],Pre[55555];
inline void Add(int x,int y,int z)
{
Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}
int Augment()
{
int Res=0x3f3f3f3f;
for(int x=Ed;x!=St;x=To[Pre[x]^1])Res=Min(Res,Val[Pre[x]]);
for(int x=Ed,i;x!=St;x=To[i^1])Val[i=Pre[x]]-=Res,Val[i^1]+=Res;
return Res;
}
int ISAP()
{
Cnt[0]=Ed;
int Flow=0,x=St;
while(Dis[x]<Ed)
{
if(x==Ed)Flow+=Augment(),x=St;
bool Flag=false;
for(int i=Cur[x],y;i;i=Next[i])
if(Val[i]&&Dis[x]==Dis[y=To[i]]+1)
Flag=true,Pre[y]=Cur[x]=i,x=y,i=0;
if(Flag)continue;
int Wd=Ed-1;
for(int i=Head[x];i;i=Next[i])
if(Val[i])Wd=Min(Wd,Dis[To[i]]);
if(!--Cnt[Dis[x]])break;
++Cnt[Dis[x]=Wd+1],Cur[x]=Head[x];
if(x!=St)x=To[Pre[x]^1];
}
return Flow;
}
int main()
{
scanf("%d%d",&n,&m),St=n+m+1,Ed=St+1;
int Ans=0;
for(int i=1,p;i<=n;++i)
scanf("%d",&p),Add(i,Ed,p);
for(int i=1,a,b,c;i<=m;++i)
{
scanf("%d%d%d",&a,&b,&c);
Add(i+n,a,0x3f3f3f3f);
Add(i+n,b,0x3f3f3f3f);
Add(St,i+n,c),Ans+=c;
}
int MaxFlow=ISAP();
printf("%d
",Ans-MaxFlow);
return 0;
}