【CodeChef CHEFBOOK】Chefbook(线性规划)(对偶原理)(费用流)(差分约束)

传送门


由于有一个判负环的步骤,所以费用流里面求最短路可以直接用Johnson算法。

然后我Johnson敲挂了T了五次。。。

题解:

考虑实际上就是给出了 2 m 2m 2m个限制:

L i ≤ p u i − q v i ≤ R i L_i\leq p_{u_i}-q_{v_i}\leq R_i LipuiqviRi

有没有解可以利用差分约束。

拆开看一下是一个线性规划:

l i m i t s : p u i − q v i ≤ R i q v i − p u i ≤ L i m a x i m i m a l : ∑ u o u t u p u − i n u q u \begin{aligned} limits:&&&&&&p_{u_i}-q_{v_i}\leq R_i\\ &&&&&&q_{v_i}-p_{u_i}\leq L_i\\ maximimal:&&&&&&\sum_{u}out_up_u-in_uq_u \end{aligned} limits:maximimal:puiqviRiqvipuiLiuoutupuinuqu

其中 o u t u out_u outu u u u的出度, i n u in_u inu u u u的入度。

感觉像是一个网络流的形式,但是不好建图,对偶一下:

l i m i t s : ∀ u , ∑ ( u , v ) ∈ E x u , v − y u , v ≥ o u t u ∀ v , ∑ ( u , v ) ∈ E y u , v − x u , v ≥ − i n v m i n i m i m a l : ∑ i R i x i + L i y i \begin{aligned} limits:&&&&&&\forall u,\sum_{(u,v)\in E}x_{u,v}-y_{u,v}\geq out_u\\ &&&&&&\forall v,\sum_{(u,v)\in E}y_{u,v}-x_{u,v}\geq -in_v\\ minimimal:&&&&&& \sum_{i}R_ix_i+L_iy_i \end{aligned} limits:minimimal:u,(u,v)Exu,vyu,voutuv,(u,v)Eyu,vxu,vinviRixi+Liyi

这里网上另一篇题解是写错了的:https://blog.csdn.net/geotcbrl/article/details/74779985

把所有限制加起来得到 0 ≥ 0 0\geq 0 00,则所有等号必须取到,考虑利用流量平衡来实现这一点。

然后可以建图跑网络流了,每个点拆成两个,一个入点一个出点,原点向入点连容量为该点入度的边,出点向汇点连容量为出度的边。

然后对于每个变量 x , y x,y x,y,我们把它当做一条边,按照符号决定方向,负号为出正号为入,容量为INF,费用为最小化式子里面带的权,然后跑最小费用最大流。

如果没有满流,或者出现负环则无解,出现负环意味着原差分约束无解。

否则有解,我们看一下流了哪些边,表示对应 p , q p,q p,q的约束取了等号,加到差分约束里面跑就行了。

可以把差分约束源点向外连的点的权值设置大一点避免出现负数。

或者也可以跑完差分约束之后减去全局最小值。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;bool f=false;
		while(!isdigit(c=gc()))f=c=='-';num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=250,INF=0x3f3f3f3f;

namespace NetWork{
	int n,S,T;
	struct edge{int to,rev,cap,w;};
	typedef std::vector<edge>::iterator iter;
	std::vector<edge> G[N];iter cur[N];
	int h[N],dis[N];bool vis[N];
	
	inline void init(int _n){
		S=_n-1,T=_n,n=_n;memset(h+1,0,sizeof(int)*n);
		for(int re i=1;i<=n;++i)G[i].clear();
	}
	inline void adde(int u,int v,int cap,int cost){
		G[u].push_back((edge){v,G[v].size(),cap,cost});
		G[v].push_back((edge){u,G[u].size()-1,0,-cost});
	}
	inline bool check(){//negative circle and connectivity
		static short cnt[N];
		memset(vis+1,0,sizeof(bool)*n);
		memset(dis+1,0x3f,sizeof(int)*n);
		std::deque<int> q(1,T);dis[T]=0,cnt[T]=0;
		while(!q.empty()){
			int u=q.front();q.pop_front();vis[u]=false;
			for(auto e:G[u])
			if(G[e.to][e.rev].cap&&dis[e.to]>dis[u]-e.w){
				dis[e.to]=dis[u]-e.w;
				if((cnt[e.to]=cnt[u]+1)>n)return true;
				if(!vis[e.to]){
					if(!q.empty()&&dis[e.to]<dis[q.front()])q.push_front(e.to);
					else q.push_back(e.to);
					vis[e.to]=true;
				}
			}
		}
		if(dis[S]>=INF)return true;
		for(int re i=1;i<=n;++i)if(dis[i]<INF)h[i]+=dis[i];
		return false;
	}
	inline bool Dij(){
		memset(dis+1,0x3f,sizeof(int)*n);
		memset(vis+1,0,sizeof(bool)*n);
		std::priority_queue<pii,std::vector<pii>,std::greater<pii> > q;
		q.push(pii(dis[T]=0,T));
		while(!q.empty()){
			int u=q.top().se;q.pop();
			if(vis[u])continue;vis[u]=true;
			for(auto e:G[u])if(G[e.to][e.rev].cap){
				int w=-e.w+h[u]-h[e.to];
				if(dis[e.to]>dis[u]+w)q.push(pii(dis[e.to]=dis[u]+w,e.to));
			}
		}
		return dis[S]<INF;
	}
	int dfs(int u,int flow){
		if(u==T)return flow;
		vis[u]=true;int ans=0;
		for(iter e=G[u].begin();e!=G[u].end();++e)
		if(e->cap&&!vis[e->to]&&
		dis[e->to]+e->w-h[u]+h[e->to]==dis[u]){
			int delta=dfs(e->to,std::min(flow-ans,e->cap));
			if(!delta){vis[e->to]=true;continue;}
			e->cap-=delta;
			G[e->to][e->rev].cap+=delta;
			if((ans+=delta)==flow)break;
		}
		vis[u]=false;return ans;
	}
	inline ll Flow(int expected){
		if(check())return -1;
		int tot_flow=0;ll tot_cost=0;
		while(Dij()){
			memset(vis+1,0,sizeof(bool)*n);
			int delta=dfs(S,INF);
			for(int re i=1;i<=n;++i)if(dis[i]<INF)h[i]+=dis[i];
			tot_flow+=delta,tot_cost+=(ll)delta*h[S];
		}
		return tot_flow==expected?tot_cost:-1;
	}
}

namespace Diff{
	int n;
	struct edge{int to,w;};
	std::vector<edge> G[N];
	int dis[N];bool vis[N];
	inline void init(int _n){
		n=_n;for(int re i=1;i<=n;++i)G[i].clear();
	}
	inline void adde(int u,int v,int w){
		G[u].push_back((edge){v,w});
	}
	inline void SPFA(int S){
		memset(dis+1,0x3f,sizeof(int)*n);
		memset(vis+1,0,sizeof(bool)*n);
		std::deque<int> q;q.push_back(S);dis[S]=0;
		while(!q.empty()){
			int u=q.front();q.pop_front();vis[u]=false;
			for(auto e:G[u])if(dis[e.to]>dis[u]+e.w){
				dis[e.to]=dis[u]+e.w;
				if(!vis[e.to]){
					if(!q.empty()&&dis[e.to]<dis[q.front()])q.push_front(e.to);
					else q.push_back(e.to);
					vis[e.to]=true;
				}
			}
		}
	}
}
int n,m;
int in[N],out[N];
inline void solve(){
	n=gi(),m=gi();ll ans=0;
	NetWork::init(2*n+2);Diff::init(2*n+1);
	memset(in+1,0,sizeof(int)*n);
	memset(out+1,0,sizeof(int)*n);
	for(int re i=1;i<=m;++i){
		int u=gi(),v=gi(),w=gi();
		++out[u],++in[v];ans+=w;
		int s=gi(),t=gi();
		NetWork::adde(v+n,u,INF,t-w);
		NetWork::adde(u,v+n,INF,w-s);
		Diff::adde(v+n,u,t-w);
		Diff::adde(u,v+n,w-s);
	}
	for(int re i=1;i<=n;++i){
		NetWork::adde(i,NetWork::T,out[i],0);
		NetWork::adde(NetWork::S,i+n,in[i],0);
	}
	ll res=NetWork::Flow(m);
	if(res==-1){cout<<"Unlike\n";return ;}
	cout<<(ans+res)<<"\n";
	for(int re u=1;u<=2*n;++u)
	for(auto e:NetWork::G[u]){
		if(NetWork::G[e.to][e.rev].cap)
		Diff::adde(e.to,u,-e.w);
	}
	for(int re i=1;i<=2*n;++i){
		Diff::adde(2*n+1,i,1e6);
		Diff::adde(i,2*n+1,0);
	}Diff::SPFA(2*n+1);
	for(int re i=1;i<=n;++i)cout<<Diff::dis[i]<<" ";cout<<"\n";
	for(int re i=1;i<=n;++i)cout<<Diff::dis[i+n]<<" ";cout<<"\n";
}
signed main(){
#ifdef zxyoi
	freopen("chefbook.in","r",stdin);
#endif
	int T=gi();while(T--)solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值