• [BZOJ1497/Luogu4174][NOI2006]最大获利


    题目链接:

    BZOJ1497

    Luogu4174

    最大权闭合子图应用。

    对于每一个中转站,向汇点连边,容量为费用的绝对值。

    对于每一个用户,向源点连边,容量为收益。同时向他需要的中转站连边,容量为(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;
    }
    
  • 相关阅读:
    jquery两个滚动条样式
    js双层动画幻灯
    漂浮QQ
    js物理弹性窗口
    js抽奖跑马灯程序
    经典算法
    判断手机浏览器终端设备
    javascript判断手机旋转横屏竖屏
    【转】处理百万级以上的数据提高查询速度的方法
    Linux -- Centos 下配置LNAMP 服务器环境
  • 原文地址:https://www.cnblogs.com/LanrTabe/p/10168726.html
Copyright © 2020-2023  润新知