POJ2175-消圈算法

题意:

一个城市中有n个大楼,m个避难所,接下来给出n个大楼的坐标和人数,接下来m行给出每个避难所的坐标和容纳量,大楼到避难所的距离定义为曼哈顿距离+1,然后给出n行m列数据,第i行第j列代表从第i个大楼到第j个避难所去的人数,保证此方案合法,然后判断此方案是否所有人到避难所的距离和最小,是的话输出OPTIMAL,否则给出更好的方案,但不一定是最优的;

思路:

如果用最小费用最大流算法直接求解,会超时,此时有个更巧妙的算法,因为题目中已经给出了一套方案,然后就利用给出的方案建立残留网络图,寻找是否存在负圈,存在的话肯定不是最优,找到这个负圈,顺着负圈增广,得到一个更优(不一定最优)的解就行了,题目不要求最优。

所谓消圈定理,就是在某个流 中,如果其对应的残余网络(剩余流量为 0 的边视为不存在)没有负圈,那它一定就是当前流量下的最小费用流。反之亦然。即「 是最小费用流等价于其残余网络中没有负圈」。

建图是关键。建残量网络,每条边只需要一个剩余流量remains和代价cost即可,剩余流量为0意味着残量网络中不存在这条边。remains(i, j)表示 i 到 j 的残量,那么
在这里插入图片描述
比如上面这个边,u到v容量是11,流量是5,那么remain(u , v) = 11 - 5 = 6; 而remian(v , u) = 5(cap(v , u) = 0, flow(v , u) = -5)。对于正向弧(i,j),flow(i,j) = remain(j,i)。只要把remains更新好,最后输出flow时转成反向弧输出就好了。
建筑 i 到防空洞 j 的容量可设为INF,输入给出了流量,则remains(i , j) = INF - f, cost = …;
相应反向弧remains(j , i) = f,cost取反;
建立源点s, 设建筑 i 的流出的流量和为tot1[i], 而s 到 i 的容量为B[i].c(建筑i的人数),其残量为B[i].c - tot1[i].。
汇点同理。

邻接表

#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 200+5;
int n, m;
int vis[maxn], dis[maxn], pre[maxn], ans[maxn][maxn];

struct Point{
	int x,y,c;
}B[maxn], S[maxn];

// 图
struct Edge{
	int u,v,flow,cost;
	Edge(int a, int b, int c, int d):u(a),v(b),flow(c),cost(d){}
}; 
vector<Edge> edges;
vector<int> G[maxn];

void init(){
	for(int i = 0; i < maxn; ++i) G[i].clear();
	edges.clear();
}

void addEdge(int u, int v, int cost, int f1, int f2){
	edges.push_back(Edge(u,v,f1,cost));
	edges.push_back(Edge(v,u,f2,-cost));
	int m = edges.size();
	G[u].push_back(m-2);
	G[v].push_back(m-1);
}

int SPFA(int s, int t){
	int num = n+m+2;
	for(int i = 0; i < num; ++i) {
		dis[i] = INF; vis[i] = 0;
	}
	queue<int> Q;
	Q.push(t); Q.push(0);
	dis[t] = 0;
	while(!Q.empty()){
		int x = Q.front(); Q.pop();
		int cnt = Q.front(); Q.pop();
		if(cnt >= num) return x;
		vis[x] = 0;
		for(int i = 0; i < G[x].size(); ++i){
			Edge& e = edges[G[x][i]];
			if(e.flow&&dis[e.v] > dis[x] + e.cost){
				dis[e.v] = dis[x] + e.cost;
				pre[e.v] = G[x][i];
				if(!vis[e.v]) { Q.push(e.v); Q.push(cnt+1); vis[e.v] = 1; }
			}
		}
	}
	return -1;
}

void augument(int u){
	memset(vis, 0, sizeof(vis));
	while(!vis[u]){
		vis[u] = 1;
		u = edges[pre[u]].u;
	}
	int x = u;
	do{
		--edges[pre[u]].flow;
		++edges[pre[u]^1].flow;
		u = edges[pre[u]].u;
		//printf("%d ",u);
	}while(u != x);
}

void solve(int s, int t){
	memset(ans, 0, sizeof(ans));
	for(int i = 0; i < edges.size(); i+=2){
		if(edges[i^1].flow == 0||edges[i].u == s||edges[i].v == t) continue;
		ans[edges[i].u][edges[i].v-n] = edges[i^1].flow;
	} 
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < m-1; ++j) printf("%d ", ans[i][j]);
		printf("%d\n",ans[i][m-1]);
	}
}

int main()
{
	freopen("in.txt","r",stdin);
	
	while(scanf("%d%d",&n,&m) == 2&&n){
		init();
		for(int i = 0; i < n; ++i) scanf("%d%d%d",&B[i].x, &B[i].y, &B[i].c);
		for(int i = 0; i < m; ++i) scanf("%d%d%d",&S[i].x, &S[i].y, &S[i].c);
		
		// 读入plan 
		int tot1[maxn];// 记录每个建筑总的出流
		int tot2[maxn];  // 记录每个防空洞总的入流
		for(int i = 0; i < m; ++i) tot2[i] = 0;
		for(int i = 0; i < n; ++i){
			tot1[i] = 0;
			for(int j = 0; j < m; ++j){
				int f; scanf("%d",&f);
				int cost = abs(B[i].x - S[j].x) + abs(B[i].y - S[j].y) + 1;
				tot1[i] += f;
				tot2[j] += f;
				addEdge(i, j+n, cost, INF-f, f); 
			}
		}
		int s = n+m, t = n+m+1;
		for(int i = 0; i < n; ++i) addEdge(s, i, 0, B[i].c-tot1[i], tot1[i]);
		for(int i = 0; i < m; ++i) addEdge(i+n, t, 0, S[i].c-tot2[i], tot2[i]);
		
		int u = SPFA(s,t);
		if(u == -1) printf("OPTIMAL\n");
		else{
			printf("SUBOPTIMAL\n");
			//printf("%d\n",u);
			augument(u);
			solve(s, t);
		}
	}
	return 0;
}

邻接矩阵:

#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 200+5;
int n, m; // n: 建筑 m: 避难点 
int remains[maxn][maxn], cost[maxn][maxn];
int pre[maxn], vis[maxn], dis[maxn];

struct Point{
	int x,y,c;
}B[maxn], S[maxn];

int SPFA(int s, int t){
	int num = n+m+2;
	for(int i = 0; i < num; ++i) {
		dis[i] = INF; vis[i] = 0;
	}
	dis[t] = 0;
	queue<int> Q;
	Q.push(t); Q.push(0);
	while(!Q.empty()){
		int x = Q.front(); Q.pop();
		int cnt = Q.front(); Q.pop();
		if(cnt >= num) return x;
		vis[x] = 0;
		for(int i = 0; i < n+m+2; ++i){
			if(remains[x][i]&&dis[i] > dis[x] + cost[x][i]){
				dis[i] = dis[x] + cost[x][i];
				pre[i] = x;
				if(!vis[i]){ Q.push(i); Q.push(cnt+1); vis[i] = 1;}
			}
		}
	}
	return -1;
}

void augument(int u){
	memset(vis, 0, sizeof(vis));
	while(!vis[u]){
		vis[u] = 1;
		u = pre[u];
	}
	int x = u;
	do{
		--remains[pre[u]][u];
		++remains[u][pre[u]];
		u = pre[u];
	}while(u != x);
}

int main()
{
	freopen("in.txt","r",stdin);
	
	while(scanf("%d%d",&n,&m) == 2&&n){
		memset(remains, 0, sizeof(remains));
		
		for(int i = 0; i < n; ++i)scanf("%d%d%d",&B[i].x, &B[i].y, &B[i].c);
		for(int i = 0; i < m; ++i)scanf("%d%d%d",&S[i].x, &S[i].y, &S[i].c);
		
		// 读入plan 
		int tot1[maxn], tot2[maxn];
		for(int i = 0; i < m; ++i) tot2[i] = 0;
		for(int i = 0; i < n; ++i){
			tot1[i] = 0;
			for(int j = 0; j < m; ++j){
				int f; scanf("%d",&f);
				tot1[i]+= f;
				tot2[j]+= f;
				cost[i][j+n] = abs(B[i].x - S[j].x) + abs(B[i].y - S[j].y) + 1;
				cost[j+n][i] = -cost[i][j+n];
				remains[i][j+n] = INF-f;
				remains[j+n][i] = f;
			}
		}
		
		int s = n+m, t = n+m+1;
		for(int i = 0; i < n; ++i){ 
			remains[s][i] = B[i].c - tot1[i]; 
			remains[i][s] = tot1[i];
		}
		for(int i = 0; i < m; ++i){
			remains[i+n][t] = S[i].c - tot2[i];
			remains[t][i+n] = tot2[i];
		}
		
		int u = SPFA(s,t);
		if(u == -1) printf("OPTIMAL\n");
		else{
			printf("SUBOPTIMAL\n");
			//printf("%d\n",u);
			augument(u);
			for(int i = 0; i < n; ++i){
				for(int j = 0; j < m-1; ++j)	printf("%d ",remains[j+n][i]);
				printf("%d\n",remains[n+m-1][i]);
			}
		}
	}

	return 0;
}

参考:
https://blog.csdn.net/mypsq/article/details/39320887
https://blog.csdn.net/qq_34798152/article/details/53639562

猜你喜欢

转载自blog.csdn.net/CY05627/article/details/90769824