• @bzoj



    @description@

    Alice 和 Bob 居住在一个由N座岛屿组成的国家,岛屿被编号为0到N-1。某些岛屿之间有桥相连,桥上的道路是双向的,但一次只能供一人通行。其中一些桥由于年久失修成为危桥,最多只能通行两次。Alice希望在岛屿al和a2之间往返an次(从al到a2再从a2到al算一次往返)。同时,Bob希望在岛屿bl和b2之间往返bn次。这个过程中,所有危桥最多通行两次,其余的桥可以无限次通行。请问Alice和Bob能完成他们的愿望吗?

    Input
    本题有多组测试数据。
    每组数据第一行包含7个空格隔开的整数,分别为N、al、a2、an、bl、b2、bn。
    接下来是一个N行N列的对称矩阵,由大写字母组成。矩阵的i行j列描述编号i一1和j-l的岛屿间的连接情况,若为“O”则表示有危桥相连:为“N”表示有普通的桥相连:为“X”表示没有桥相连。

    Output
    对于每组测试数据输出一行,如果他们都能完成愿望输出“Yes”,否则输出“No”。

    Sample Input
    4 0 1 1 2 3 1
    XOXX
    OXOX
    XOXO
    XXOX
    4 0 2 1 1 3 2
    XNXO
    NXOX
    XOXO
    OXOX
    Sample Output
    Yes
    No

    数据范围
    4<=N<50
    O<=a1, a2, b1, b2<=N-1
    1 <=an. b<=50

    @solution@

    【bzoj 的题面。。。我也不好吐槽什么。。。】

    开头先膜拜这位大佬的博客,相关证明的细节非常详细orz

    本题其实就是一个多源多汇且源汇之间的匹配固定的最大流。
    可以比较容易地想到这样一个思路:建超级源点连 a1, b1,将 a2, b2 连向超级汇点,容量分别设为 2an 与 2bn;将危桥的容量设为 2、正常的桥容量设为 inf,连成双向边。
    这样建图后跑最大流,如果最大流 = 2*(an + bn) 则有解。
    因为所有边的容量都为偶数(可以把 inf 当作偶数),所以可以把容量全部除以 2,判断最大流是否等于 an + bn(即网上所说只需要把往返看作单次)。

    但是可以发现如果 a1 -> b2 且 b1 -> a2,则最大流跑出来的 “有解” 不一定真的有解。
    还有一点,危桥是限制往返的次数加起来 <= 1,但我们建出来的图相当于正方向与反方向分别 <= 1。

    我们只需要将源点 s 连 a1, b2,将 a2, b1 连汇点 t,再跑一次最大流,判定是否合法即可。
    至于证明。。。上面说的那篇博客讲得非常好所以我就直接粘过来了

    如果满流且仍然存在问题一那种情况呢?画个图。
    假设第一次跑最大流, a1→b2 的流量为 x ,那么 b1→b2 的流量为 bn−x, b1→a2 的流量也是 x , a1→a2 的流量是 an−x。
    而第二次跑最大流,因为是无向图, a1→a2 和 b2→b1 的流量可以不变,还是 an−x,bn−x。那么 a1→b1 和 b2→a2 的流量(注:原博主这个地方是写错了,下面的评论区中有提到)也都还是 x 。
    而这两次说明了什么呢, a1 可以流到 b1 x 流量,还可以流到 b2 x 流量,同时不影响 a1 与 a2, b1 与 b2 之间的流量。
    因为是无向图,将 a1→b1 的流量反向,就可以得到 b1→b2 x 的流量。 b1,b2 之间的流就合法了。同理 a1,a2 之间的流也合法。
    所以如果交换 b1, b2 后仍满流,一定不存在问题一那种情况。

    对于问题二,假如 a1→a2 正向经过了一座危桥,而 b1→b2 反向经过了这座桥,那么交换 b1,b2,以 b2 为起点后, a1→a2,b2→b1 两条路径都是正向通过了这条边,就受到了流量的限制。
    所以如果仍满流,不存在问题二。

    @accepted code@

    #include<cstdio>
    #include<queue>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int MAXN = 50;
    const int MAXV = MAXN;
    const int MAXE = 2*MAXV*MAXV;
    const int INF = int(1E6);
    struct FlowGraph{
    	struct edge{
    		int to, cap, flow;
    		edge *nxt, *rev;
    	}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
    	int s, t, n;
    	void clear(int _n) {
    		n = _n; ecnt = &edges[0];
    		for(int i=0;i<=n;i++)
    			adj[i] = NULL;
    	}
    	void addedge(int u, int v, int c) {
    		edge *p = (++ecnt), *q = (++ecnt);
    		p->to = v, p->cap = c, p->flow = 0;
    		p->nxt = adj[u], adj[u] = p;
    		q->to = u, q->cap = 0, q->flow = 0;
    		q->nxt = adj[v], adj[v] = q;
    		p->rev = q, q->rev = p;
    //		printf("%d %d %lld
    ", u, v, c);
    	}
    	queue<int>que; int d[MAXV + 5];
    	bool relabel() {
    		for(int i=0;i<=n;i++)
    			cur[i] = adj[i], d[i] = n + 5;
    		while( !que.empty() ) que.pop();
    		d[t] = 0, que.push(t);
    		while( !que.empty() ) {
    			int f = que.front(); que.pop();
    			for(edge *p=adj[f];p;p=p->nxt)
    				if( d[f] + 1 < d[p->to] && p->rev->cap > p->rev->flow ) {
    					d[p->to] = d[f] + 1;
    					que.push(p->to);
    					if( p->to == s ) return true;
    				}
    		}
    		return !(d[s] == n + 5);
    	}
    	int aug(int x, int tot) {
    		if( x == t ) return tot;
    		int sum = 0;
    		for(edge *&p=cur[x];p;p=p->nxt) {
    			if( p->cap > p->flow && d[p->to] + 1 == d[x] ) {
    				int del = aug(p->to, min(p->cap - p->flow, tot - sum));
    				p->flow += del, p->rev->flow -= del, sum += del;
    				if( sum == tot ) break;
    			}
    		}
    		return sum;
    	}
    	int max_flow(int _s, int _t) {
    		s = _s, t = _t; int flow = 0;
    		while( relabel() )
    			flow += aug(s, INF);
    		return flow;
    	}
    }G;
    int N, a1, a2, an, b1, b2, bn;
    char str[MAXN + 5][MAXN + 5];
    int main() {
    	while( scanf("%d%d%d%d%d%d%d", &N, &a1, &a2, &an, &b1, &b2, &bn) == 7 ) {
    		bool flag = true;
    		for(int i=0;i<N;i++)
    			scanf("%s", str[i]);
    		G.s = N, G.t = N + 1;
    		G.clear(N + 2);
    		for(int i=0;i<N;i++)
    			for(int j=0;j<N;j++)
    				if( str[i][j] == 'O' ) G.addedge(i, j, 1);
    				else if( str[i][j] == 'N' ) G.addedge(i, j, INF);
    		G.addedge(G.s, a1, an), G.addedge(G.s, b1, bn);
    		G.addedge(a2, G.t, an), G.addedge(b2, G.t, bn);
    		flag &= (G.max_flow(G.s, G.t) == an + bn);
    		G.s = N, G.t = N + 1;
    		G.clear(N + 2);
    		for(int i=0;i<N;i++)
    			for(int j=0;j<N;j++)
    				if( str[i][j] == 'O' ) G.addedge(i, j, 1);
    				else if( str[i][j] == 'N' ) G.addedge(i, j, INF);
    		G.addedge(G.s, a1, an), G.addedge(G.s, b2, bn);
    		G.addedge(a2, G.t, an), G.addedge(b1, G.t, bn);
    		flag &= (G.max_flow(G.s, G.t) == an + bn);
    		if( flag ) puts("Yes");
    		else puts("No");
    	}
    }
    

    @details@

    听说多源多汇且固定源汇的问题只能用线性规划解。大概是因为这道题非常特殊吧(2 源 2 汇)。

    总感觉这个题的结论非常神仙,不是我们凡人可以接触的领域。
    听说是从topcoder上搬来的题。

  • 相关阅读:
    php 0324
    php 0323
    php 0322
    php 0325
    php 0308
    php 0311
    php 0312
    php 0310
    爬虫实践03 | xpath爬取通州区人民政府网站招聘信息
    用友U8 | 我是如何解决序列号状态出错问题的?
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11416181.html
Copyright © 2020-2023  润新知