#负环,费用流#ZOJ 1553 Evacuation Plan

题目

一个城市有 \(n\) 座建筑物,每个建筑物里面有一些人,为了在战争爆发时这些人都可以避难,城市里面建了 \(m\) 座避难所。每座避难所只能容纳有限人数。

给出每个建筑物的坐标 \((x_i,y_i)\) 和避难所的坐标 \((p_j,y_j)\)。一个人从第 \(i\) 个建筑物逃离到第 \(j\) 个避难所花费的时间是 \(|x_i-p_j|+|y_i-q_j|+1\)

现在给你一种避难方案,问你这种方案是否是时间总数最小的?如果不是,请输出一种比当前更优的方案(不一定最优)。


分析

这个费用流的模型很容易建出来,并将当前流量赋以给出的避难方案的流量,如果比当前更优,说明原图存在负环可使流量不变而费用减少,

那么对残量网络找负环,找到负环那么用负环更新答案,否则就是不存在更优解。


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
using namespace std;
const int N=216,inf=0x3f3f3f3f; queue<int>q; struct node{int y,w,f,next;}e[N*N];
int as[N],dis[N],v[N],_T,dep[N],cnt[N],x[N],y[N],C[N],X[N],Y[N],a[N],A[N],b[N][N],c[N],pre[N],n,m,S,T,et=1,ans,ANS,Test;
void add(int x,int y,int w,int W,int f){
	e[++et]=(node){y,w-W,f,as[x]},as[x]=et;
	e[++et]=(node){x,W,-f,as[y]},as[y]=et;
}
int Abs(int x){return x<0?-x:x;}
int spfa(){
	for (int i=1;i<=_T;++i) dis[i]=inf,v[i]=cnt[i]=dep[i]=0;
    while (!q.empty()) q.pop();
	dis[_T]=0,v[_T]=1,q.push(_T);
	while (!q.empty()){
		int x=q.front(); q.pop();
		if (++cnt[x]==_T) return x;
		for (int i=as[x];i;i=e[i].next)
		if (e[i].w&&dis[e[i].y]>dis[x]+e[i].f){
			dis[e[i].y]=dis[x]+e[i].f,dep[e[i].y]=dep[x]+1,pre[e[i].y]=x;
			if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
		}
		v[x]=0;
	}
	return 0;
}
int main(){
    while (scanf("%d%d",&n,&m)==2){
		S=n+m+1,T=S+1,_T=T+1,et=1;
		for (int i=1;i<=n;++i) scanf("%d%d%d",&x[i],&y[i],&a[i]);
		for (int i=1;i<=m;++i) scanf("%d%d%d",&X[i],&Y[i],&A[i]);
		for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j){
			scanf("%d",&b[i][j]);
			add(i,j+n,inf,b[i][j],Abs(x[i]-X[j])+Abs(y[i]-Y[j])+1);
			c[i]+=b[i][j],C[j]+=b[i][j];
		}
		for (int i=1;i<=n;++i) add(S,i,a[i],c[i],0);
		for (int i=1;i<=m;++i) add(i+n,T,A[i],C[i],0);
		for (int i=1;i<=T;++i) add(_T,i,1,0,0);
		if (!(ans=spfa())) printf("OPTIMAL\n");
		else{
			printf("SUBOPTIMAL\n");
			for (int i=1;i<=_T;++i) v[i]=0;
			while (!v[ans]) v[ans]=1,ans=pre[ans];
			int t=ans;
			do{
				if (t<=n&&pre[t]<S) ++b[t][pre[t]-n];
				if (pre[t]<=n&&t<S) --b[pre[t]][t-n];
				t=pre[t];
			}while (t!=ans);
			for (int i=1;i<=n;++i)
			for (int j=1;j<=m;++j) printf("%d%c",b[i][j],j==m?10:32);
		}
		for (int i=1;i<=_T;++i) c[i]=v[i]=C[i]=pre[i]=as[i]=0;
    }
    return 0;
} 
posted @ 2025-06-14 06:44  lemondinosaur  阅读(14)  评论(0)    收藏  举报