zoukankan      html  css  js  c++  java
  • POJ 2175 spfa费用流消圈

    题意:给出n栋房子位置和每栋房子里面的人数,m个避难所位置和每个避难所可容纳人数。然后给出一个方案,判断该方案是否最优,如果不是求出一个更优的方案。

    思路:很容易想到用最小费用流求出最优时间,在与原方案花费时间对比判断原方案是否最优。也许是组数太多了,这种方法会超时的。 放弃该思路。


    看看题目没要求要最优解,而是得到一个更优的解。

    原图的所有反向边中能够找到一个总费用为负的回路(而且要有流量)的话,那就该解不是最优解,把该负环消去,更新流量,得到优化后的解。(原因: 反向边保存的是已经流过的流量, 如果出现环,那么说明我们可以不走这个环,那么总的费用就变小了)。

    具体操作:从汇点出发SPFA,一个点入队次数大于顶点数时就可以判断有负圈存在了。

    特别注意:但这时第一次入队n次的这个点却未必是负圈上的。

    如数据

    1 2 w = 5

    2 3 w = -1

    3 4 w = -1

    4 2 w = -1

    我们从点1出发, 1->2->3->4->2, 到了2以后,由于存在反向边(2 1 w = -5),走反向边。

    路径为 1->2->3->4->2->    1->2->3->4,  所以最后第一次入队n次的点是2,但2不在负圈上。

    如何找到负圈上的点和负圈:

    我们可以记录下来每个点被更新的前一个点,沿这个路径不停地往回找,直到发现找到的这个点在之间已经遇到过了,那么找到的这个点就一定是某个负圈上的点了。最后以这个点为基础,回溯找到整个负圈并更新流量即可。

     

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int maxn = 303;
    const int inf = 1e9;
    
    struct node {
    	int x, y, c;
    	void in() {
    		scanf("%d%d%d", &x, &y, &c);
    	}
    } a[maxn], b[maxn];
    int sa[maxn], sb[maxn];
    int n, m;
    
    struct Edge {
    	int u, v, c, w, next;
    	Edge(int u, int v, int c, int w, int next) :
    			u(u), v(v), c(c), w(w), next(next) {
    	}
    	Edge() {
    	}
    } edge[1000006];
    int head[maxn], E;
    void init() {
    	memset(head, -1, sizeof(head));
    	E = 0;
    }
    void add(int s, int t, int c, int cc, int w) {
    	edge[E] = Edge(s, t, c, w, head[s]);
    	head[s] = E++;
    	edge[E] = Edge(t, s, cc, -w, head[t]);
    	head[t] = E++;
    }
    
    
    inline int F(int x) {
    	return x > 0 ? x : -x;
    }
    inline int Dis(int i, int j) {
    	return F(a[i].x - b[j].x) + F(a[i].y - b[j].y) + 1;
    }
    int S, T;
    bool vis[maxn];
    int dis[maxn], in[maxn], pre[maxn];
    int spfa(int s, int n) {//消负环
    	int i, u, v;
    	for(i = 0; i <= n; i++)
    		dis[i] = inf, pre[i] = -1, vis[i] = 0, in[i] = 0;
    	queue <int> q;
    	dis[s] = 0;
    	vis[s] = 1;
    	in[s]++;
    	q.push(s);
    	while(!q.empty()) {
    		u = q.front();
    		q.pop();
    		vis[u] = 0;
    		for(i = head[u]; i != -1; i = edge[i].next) {
    			v = edge[i].v;
    			if(edge[i].c && dis[v] > dis[u] + edge[i].w) {
    				dis[v] = dis[u] + edge[i].w;
    				pre[v] = i;
    				if(!vis[v]) {
    					vis[v] = 1;
    					q.push(v);
    					in[v]++;
    					if(in[v] >= n)
    						return v;
    				}
    			}
    		}
    	}
    	return -1;
    }
    
    void update(int p) {
    	int u = pre[p], i;
    	int aug = inf;
    	aug = min(aug, edge[u].c);
    	for(i = pre[edge[u].u]; i != u; i = pre[edge[i].u])
    		aug = min(aug, edge[i].c);
    	edge[u].c -= aug;
    	edge[u ^ 1].c += aug;
    	for(i = pre[edge[u].u]; i != u; i = pre[edge[i].u]) {
    		edge[i].c -= aug;
    		edge[i ^ 1].c += aug;
    	}
    }
    
    void solve() {
    	int p = spfa(T, T+1); //
    	int i, j;
    	if(p == -1) {
    		printf("OPTIMAL
    ");
    		return;
    	}
    	printf("SUBOPTIMAL
    ");
    	memset(vis, 0, sizeof(vis));
    	while(!vis[p]) {
    		vis[p] = 1;
    		p = edge[pre[p]].u;
    	}
    	update(p);
    	for(i = 0; i < n; i++) {
    		for(j = 0; j < m-1; j++)
    			printf("%d ", edge[(i * m + j)<<1^1].c);
    		printf("%d
    ", edge[(i * m + j)<<1^1].c);
    	}
    }
    int main() {
    	int i, j, z;
    	while(~scanf("%d%d", &n, &m)) {
    		S = n + m;
    		T = S + 1;
    		init();
    		for(i = 0; i < n; i++)
    			a[i].in(), sa[i] = 0;
    		for(i = 0; i < m; i++)
    			b[i].in(), sb[i] = 0;
    		//构造流完题目中可行流的残余网络
    		for(i = 0; i < n; i++)
    			for(j = 0; j < m; j++) {
    				scanf("%d", &z);
    				add(i, j + n, inf, z, Dis(i, j));
    				sa[i] += z;
    				sb[j] += z;
    			}
    		for(i = 0; i < n; i++)
    			add(S, i, a[i].c - sa[i], sa[i], 0);
    		for(i = 0; i < m; i++)
    			add(i + n, T, b[i].c - sb[i], sb[i], 0);
    		solve();
    	}
    	return 0;
    }
    



  • 相关阅读:
    uboot 环境变量
    U-boot的环境变量: bootcmd 和bootargs
    linux中的rootfs/initrd/ramfs/initramfs
    关于Linux启动时挂载rootfs的几种方式
    u-boot-2011.06在基于s3c2440开发板的移植之引导内核与加载根文件系统
    MySQL列出当前月的每一天
    SQL 标量函数-----日期函数 day() 、month()、year()
    mysql 中 DATE_ADD函数和 DATE_SUB函数用法
    mysql 中 DATE_ADD(date,INTERVAL expr type)
    mysql中date_add()函数的使用?
  • 原文地址:https://www.cnblogs.com/pangblog/p/3313329.html
Copyright © 2011-2022 走看看