首页 > 其他分享 >ASC11 A - Beer Problem

ASC11 A - Beer Problem

时间:2023-05-27 16:24:13浏览次数:41  
标签:Juc 费用 int ASC11 add 流量 Beer ans Problem

题意:给出一个无向网络,求其最大费用流(不是最大费用最大流)

首先考虑无向图怎么解决。

先尝试对每个边构造一个子结构,具体方法是对每个边 \((x,y)\) 新增两个点 \(a,b\),然后从 \((x,y)\) 分别向 \(a\) 连有向边,\(b\) 向 \((x,y)\) 连有向边。\(a\) 和 \(b\) 之间连流量为 \(f\),费用为 \(c\) 的边。这样就和无向边是一样的。

但是这会让我们的点数变成 \(O(m)\) 的。我们发现一条边其实不可能从两边都流哪怕一个流量。因为我们把两边的流量互换,因为边权是负的,所以两边都不流这个流量一定是更优的。所以我们可以直接两边连流量费用 \((f,c)\) 的有向边。但是只有最大费用流且边权是负(或最小费用流边权是正)的时候可以这么干。

然后考虑解决最大费用流。首先众所周知在非满流的情况下,费用是关于流量的凸函数。我们通过暴力计算的方法求出了在样例中不同的流量和费用的对应关系式。

(这是流量和最终答案的关系,最终答案是网络流中费用的相反数)

image

横坐标是源点流出的流量,纵坐标是费用 \(/100\)。而我们只要最大费用。我们发现,满流在 \(f=120\) 取到,这时的费用为 \(c=2900\)。在这之前,费用是单峰的且是凸的。

既然是个凸函数,就可以通过限制从源点出发的流量,然后三分法找到费用函数的顶点。

三分法的三分和建图代码如下:


int cnt=0,n,m,a[2005],b[2005],c[2005],f[2005],p[105];
inline void add_edge(int x,int y,int f,int c){
	Juc::add_edg(y,x,f,c);
	Juc::add_edg(x,y,f,c);
}
inline int check(int flow){
	Juc::flush();
	cnt=n+1;
	rep(i,1,n+1)Juc::ptt(i);
	Juc::s=0,Juc::t=n+1;
	Juc::add_edg(0,1,flow,0);
	rep(i,2,n)Juc::add_edg(i,n+1,1e9,-p[i]);
	rp(i,m)add_edge(a[i],b[i],f[i],c[i]);
	Juc::MCMF();
	return -Juc::cost;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	rep(i,2,n)cin>>p[i];
	rp(i,m)cin>>a[i]>>b[i]>>f[i]>>c[i];
	int l=0,vl=check(0),r=100000,vr=check(r),ans=max(vl,vr);
	while(r-l>3){
		int len=(r-l-1),x=l+len/3,y=r-len/3;
		int vx=check(x),vy=check(y);
		ans=max(ans,vx);
		ans=max(ans,vy);
		if(vx>=vy)r=y,vr=vy;
		else l=x,vl=vx;
	}
	rep(i,l+1,r-1)ans=max(ans,check(i));
	cout<<ans<<endl;
	return 0;
}
//Nyn-siyn-hert

可惜这样复杂度是寄的。

网络流最有有效的优化是什么呢?是残量网络优化。我们每次给源点加一的流量,然后就在残量网络跑一遍 MCMF,观察当前的费用是否比前一次更好。如果更劣了,峰就在前一个达到了,直接输出即可。但是这样相当于把费用流那个很松的 \(f\) 给卡满了,复杂度就是满的 \(O(nmf)\),也是会挂的。

	int ans=0,res=0;
	rep(i,1,100000){
		Juc::add_edg(0,1,1,0);
		Juc::MCMF();
		res=-Juc::cost;
		if(res<=ans)break;
		ans=res;
	}cout<<ans<<endl;

我们考虑从网络流本身入手。为什么网络流是凸函数呢?这和 \(Dinic\) 算法的内核有关。\(Dinic\) 算法的内核是每次沿着最短路增广,而当前流的费用就是最短路的长度。而 \(Dinic\) 的复杂度证明里面有一个“最短路不变小”。所以,实际上我们的“费用函数的导数”,也就是“每次增广之后的最短路”是单调的。所以网络流才是凸函数。

所以很明显,当我们的 \(spfa\) 跑出正的最短路的时候,我们就该发现,现在的费用函数已经来到了顶点,可以直接返回答案了。

这样就可以解决这个题。


namespace Juc{
	int n,m,s,t,a,b,c,d;
	vt<int>pnts;
	int dep[200005],vis[200005],cur[200005],dist[200005],vist[200005];
	struct edge{
		int a,b,f,c;
		edge(int _a,int _b,int _f,int _c){
			a=_a,b=_b,f=_f,c=_c;
		};
	};
	vt<edge>ve;
	vt<int>vv[200005];
	inline void add_edg(int a,int b,int c,int d){
		ve.pb(edge(a,b,c,d));
		ve.pb(edge(b,a,0,-d));
		vv[a].pb(ve.size()-2);
		vv[b].pb(ve.size()-1);
	}
	inline void ptt(int a){
		pnts.pb(a);
	}
	inline bool SPFA(){
		for(auto i:pnts)dist[i]=1e9,vist[i]=0,vis[i]=0,cur[i]=0;
		queue<int>q;
		q.push(s),vist[s]=1,dist[s]=0;
		while(q.size()){
			int i=q.front();
			q.pop();
			vist[i]=0;
			for(auto g:vv[i]){
				auto &e=ve[g];
				if(e.f&&dist[i]+e.c<dist[e.b]){
					dist[e.b]=dist[i]+e.c;
					if(!vist[e.b]){
						vist[e.b]=1;
						q.push(e.b);
					}
				}
			}
		}
		return dist[t]<0;
	}
	int cost=0;
	inline int DFS(int i,int f){
		if(i==t||f==0)return f;
		vis[i]=1;
		int res=0;
		for(int j=cur[i];j<(int)vv[i].size();j++){
			edge& e=ve[vv[i][j]];
			cur[i]=j;
			if(!vis[e.b]&&dist[e.b]==dist[i]+e.c&&e.f){
				ll flow=DFS(e.b,min(e.f,f));
				ve[vv[i][j]].f-=flow,ve[vv[i][j]^1].f+=flow;
				f-=flow,res+=flow;
				cost+=flow*e.c;
			}
			if(!f)break;
		}
		vis[i]=0;
		return res;
	}
	inline int MCMF(){
		int ans=0;
		while(SPFA()){
			ans+=DFS(s,1e9);
		}
		return ans;
	}
	inline void flush(){
		for(auto i:pnts)vv[i].clear();
		ve.clear();cost=0;
		pnts.clear(); 
	}
}
int n,m,a,b,c,f,p;
inline void add_edge(int x,int y,int f,int c){
	Juc::add_edg(y,x,f,c);
	Juc::add_edg(x,y,f,c);
}
using namespace FastIO;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	n=in(),m=in();
	rep(i,2,n){
		p=in();
		Juc::add_edg(i,n+1,1e9,-p);
	}
	rp(i,m){
		a=in(),b=in(),f=in(),c=in();
		add_edge(a,b,f,c);
	}
	rep(i,1,n+1)Juc::ptt(i);
	Juc::s=0,Juc::t=n+1;
	Juc::add_edg(0,1,1e9,0);
	Juc::MCMF();
	out(-Juc::cost)('\n');
	return 0;
}
//Nyn-siyn-hert

标签:Juc,费用,int,ASC11,add,流量,Beer,ans,Problem
From: https://www.cnblogs.com/jucason-xu/p/17436892.html

相关文章

  • ASC11
    上来先开了D,比较简单的小题D-IntegerNumbers题意:现在有一个序列,改变最少的位置,使得这个序列是等差数列,输出方案。我们发现,如果我们不改变\(a_i\),那么只有满足\(a_i-a_j=i-j\)的\(j\)是不需要改变的。也就是需要改变的数是\(n\)减去\(a_i-a_j=i-j\)的个数(\(i\)可......
  • ZOJ 3959 Problem Preparation
    传送门根据题目描述写,对于每组给定的数据判断是否满足四个要求就可以了。#include<bits/stdc++.h>usingnamespacestd;intx[120];intmain(){//freopen("in.txt","r",stdin);cin.tie(0);cout.tie(0);intt;cin>>t;while(t--){......
  • Problem F: STL——集合运算
    HomeWebBoardProblemSetStandingStatusStatisticsProblemF:STL——集合运算TimeLimit:1Sec  MemoryLimit:128MBSubmit:3767  Solved:1927[Submit][Status][WebBoard]Description集合的运算就是用给定的集合去指定新的集合。设A和B是集合,则它们的并......
  • Problem B: 时间和日期类(II)
    HomeWebBoardProblemSetStandingStatusStatisticsProblemB:时间和日期类(II)TimeLimit:4Sec  MemoryLimit:128MBSubmit:2673  Solved:1980[Submit][Status][WebBoard]Description设计一个日期时间类,用于读取输入的数据,按格式输出日期和时间......
  • Problem A: 时间和日期类(I)
    HomeWebBoardProblemSetStandingStatusStatisticsProblemA:时间和日期类(I)TimeLimit:4Sec  MemoryLimit:128MBSubmit:4223  Solved:2418[Submit][Status][WebBoard]Description设计一个时间类和一个日期类,用于读取输入的数据,按......
  • Problem E: STL——灵活的线性表
    HomeWebBoardProblemSetStandingStatusStatisticsProblemE:STL——灵活的线性表TimeLimit:1Sec  MemoryLimit:128MBSubmit:5192  Solved:2169[Submit][Status][WebBoard]Description数组和链表是我们熟知的两种线性结构,但是它们不够......
  • Problem C: 重载字符的加减法
    HomeWebBoardProblemSetStandingStatusStatisticsProblemC:重载字符的加减法TimeLimit:1Sec  MemoryLimit:128MBSubmit:1895  Solved:1155[Submit][Status][WebBoard]Description定义一个字符类Character,只有一个char类型的数据成员。重载......
  • Problem G: STL——Jerry的问题
    HomeWebBoardProblemSetStandingStatusStatisticsProblemG:STL——Jerry的问题TimeLimit:1Sec  MemoryLimit:128MBSubmit:3033  Solved:1888[Submit][Status][WebBoard]Description最近Jerry正在刻苦的学习STL中的set的功能函数,他发......
  • Problem I: STL——括号匹配
    HomeWebBoardProblemSetStandingStatusStatisticsProblemI:STL——括号匹配TimeLimit:1Sec  MemoryLimit:128MBSubmit:3032  Solved:1855[Submit][Status][WebBoard]Description给出一堆括号,看其是否匹配,例如()、()()、(())这样的......
  • Problem C: 数量的类模板
    HomeWebBoardProblemSetStandingStatusStatisticsProblemC:数量的类模板TimeLimit:1Sec  MemoryLimit:128MBSubmit:1173  Solved:812[Submit][Status][WebBoard]Description定义一个类模板Data,用于包装C++中的基本数据类型int和double。它......