BSOJ6202题解

来一个瞎几把口胡的费用流做法

首先每个点向目标点连边,显然是一个二分图完备匹配。

然后显然直接跑 MCMF 是会寄掉的。

不妨来想一想怎么让费用流模型帮我们“自动”计算费用。

因为绕着一个环的那个绝对值的最小值相当于最短路,所以我们可以对于每个桌子,连一条边 \((i,(i+1)\bmod n,n)\)(最坏有 \(n\) 个节点会经过这条边)

然后在桌子与桌子之间连边。

具体地

对于 \(nm\) 个位置,每个位置连每张桌子上对应的位置

每个桌子内部有 \(2m\) 条边。

节点数量是 \(2nm+2\),边数量是 \(n^2m+2nm\)

也许是可以跑过去的。(\(6002\) 个点和 \(900600\) 条边)

而且还有分层,边权也很有规律。。。

需要注意的是此图存在负环,需要处理一下细节。

好像数据有点儿水,直接跑就能过(

以及不知道为什么要把前向星换成 vector 才能过。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops","-fdelete-null-pointer-checks")
#include<cstdio>
#include<vector>
#include<queue>
const int M=305,N=15;
int n,m,s,t,tot,l[M][N],r[M][N],in[M][N],out[M][N];
int d[6005];bool iq[6005],vis[6005];
std::deque<int>q;int flow,ret;
inline int min(const int&a,const int&b){return a>b?b:a;}
inline int abs(const int&a){return a>0?a:-a;}
struct Edge{
	int v,w,flow,id;
};std::vector<Edge>G[6005];
inline void Add(const int&u,const int&v,const int&flow,const int&w){
	G[u].push_back((Edge){v,w,flow,G[v].size()});
	G[v].push_back((Edge){u,-w,0,G[u].size()-1});
}
inline bool BFS(){
	for(int i=s;i<=t;++i)d[i]=0x7fffffff,vis[i]=false;d[s]=0;q.push_back(s);
	while(!q.empty()){
		const int u=q.front();int v;iq[u]=false;q.pop_front();
		for(Edge&E:G[u])if(E.flow){
			const int&w=d[u]+E.w;
			if(w<d[v=E.v]){
				d[v]=w;if(iq[v])continue;iq[v]=true;
				if(!q.empty()&&d[v]<d[q.front()])q.push_front(v);else q.push_back(v);
			}
		}
	}
	return d[t]!=0x7fffffff;
}
inline int DFS(const int&u,const int&flow){
	if(u==t)return flow;
	int v,ret(flow);vis[u]=true;
	for(Edge&E:G[u])if(d[v=E.v]==d[u]+E.w&&E.flow&&!vis[v]){
		const int&F=DFS(v,min(ret,E.flow));if(!F)d[v]=-0x7fffffff;E.flow-=F;G[v][E.id].flow+=F;ret-=F;
		if(!ret)return flow;
	}
	return flow-ret;
}
inline void Dinic(){
	while(BFS())while(const int&F=DFS(s,n*m))ret+=F*d[t],flow+=F;
	if(flow!=n*m)printf("no solution");else printf("%d",ret);
}
signed main(){
	scanf("%d%d",&n,&m);
	s=++tot;for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",l[i]+j),++l[i][j],in[i][j]=++tot;
	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",r[i]+j),++r[i][j],out[i][j]=++tot;t=++tot;
	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
		Add(s,in[i][j],1,0);Add(out[i][j],t,1,0);Add(out[i][j],out[i][j%m+1],m,1);Add(out[i][j%m+1],out[i][j],m,1);
		for(int k=l[i][j];k<=r[i][j];++k)Add(in[i][j],out[k][j],1,abs(i-k)<<1);
	}
	Dinic();
}
posted @ 2022-05-14 08:19  Prean  阅读(13)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};