POJ 3057 二分图匹配 + dinic

题意:

给 X * Y 大小的地图, ‘ X ' 表示墙壁, ' . ' 表示空区域, ’ D '表示门  

每个空区域一开始都有一个人, 每个人可以向四个方向前进一步或者原地停留, 所有的人都需要从门中走出, 且一扇门单一时间只能通过一个人

问 所有人从门中走出的最短时间

思路:

这是放在挑战上的一个例题,由于这道题在书中紧跟着最小费用流, 所以一开始尝试从费用流的角度求解这道题, 然而过了两三天忘了当时怎么想的了

1. 首先考虑直接以图的连通关系建图跑网络流, 会遇到以下问题(并没有代码实现过, 瞎扯扯)

AB非门的条件下, 如果A与B连通, 显然A到B边的流量为inf, B到A边的流量也为inf, 那么反向边似乎变成了另外一种形式

在这种建图方式下, 无法表现时间这个限制条件

2. 挑战的建图方式是将一个门拆成多个时间版本的门, 然后对各个时间版本的图进行二分图匹配看最大流是否等于图中人数

挑战上不建议使用二分时间的方式来做, 显然是重复跑了非常多的东西(建图这种花销还是小事了)

每次将时间+1, 从上个时间对应的图的最大流直接更新出现有版本的最大流在最大流问题中 似乎这样比二分快耶

3. 有一个没有证实过的思路

条件: 显然能直接到某个门的点只有一个(仅需1的时间就能到达相应的门)

对于每个时间我们可以这样建图: 

假设在时间为 t 的条件下 从空区域A 我们可以到达门D  且设 “门前点” 为B

超级源点S -> 空区域A cap = 1;

A  -> B cap = 1 ;

B  -> D cap = t ;

D -> 超级汇点T cap = inf;

这个思路没有实现过,但本质上和挑战的方法是一样的, 我认为是可行的方案

以下为根据挑战写的代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <stack>
#include <cctype>
#include <cmath>
#include <vector>
#include <sstream>
#include <bitset>
#include <deque>
#include <iomanip>
using namespace std;
#define pr(x) cout << "x = " << x << endl;
#define bug cout << "bugbug" << endl;
#define ppr(x, y) printf("(%d, %d)\n", x, y);
#define pfun(a, b) printf("x = %d   f(x) = %d\n", a, b);

typedef long long ll;
typedef pair<int, int> P;
const int MOD = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 1e1 + 4;
const int maxm = 1e2 + 4;
char mp[maxn][maxn];
int tot, dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int ord[maxn][maxn], Tmp;

vector<P> E[maxm];
queue<P> q;
bool used[5100];
int level[5100], iter[5100], n, m, u, v, cab;
bool check(int x, int y){
	return x > 0 && x <= n && y > 0 && y <= m; 
}
int vis[maxn][maxn];
void bfs(int x, int y, int beg){
//	cout << "begin from "; 
//	ppr(x, y);
//	cout << "begin with " << beg << endl; 
	while(q.size()) q.pop();
	q.push(P(x, y));
	memset(vis, -1, sizeof vis);
	vis[x][y] = 0;
	while(q.size()){
		P top = q.front(); q.pop();
		x = top.first, y = top.second;
		for (int i = 0; i < 4; ++i){
			int nx = x + dir[i][0], ny = dir[i][1] + y;
			if (check(nx, ny) && mp[nx][ny] == '.' && -1 == vis[nx][ny]){
				vis[nx][ny] = vis[x][y] + 1;
				q.push(P(nx, ny));
//				ppr(nx, ny); cout << ord[nx][ny] << endl;
				for (int j = vis[nx][ny]; j <= tot; ++j){
					E[j].push_back(P(ord[nx][ny], beg + j));
//					ppr(ord[nx][ny], beg + j);
				}
			}
		}
	}
	return;
} 
struct Edge{
    int to, cab, rev;
    Edge(int _to, int _cap, int _rev):to(_to), cab(_cap), rev(_rev){

    }
};
vector<Edge> G[5100];
inline void add(int u, int v, int cap){
    G[u].push_back(Edge(v, cap, G[v].size()));
    G[v].push_back(Edge(u, 0, G[u].size() - 1));
    return;
} 
void bfs(int s, int t){
    level[s] = 0;
    queue<int> q;
    q.push(s);
    while(q.size()){
        int u = q.front(); q.pop();
        for (int i = 0; i < G[u].size(); ++i){
            int to = G[u][i].to;
            if (level[to] == -1 && G[u][i].cab > 0){
                level[to] = level[u] + 1;
                q.push(to);
            }
        }
    }
    return;
}
int dfs(int u, int t, int f){
    if (u == t) return f;
    for (int& i = iter[u]; i < G[u].size(); ++i){
        int to = G[u][i].to;
        if (level[to] > level[u] && G[u][i].cab > 0){
            int d = dfs(to, t, min(f, G[u][i].cab));
            if (d > 0){
                G[u][i].cab -= d;
                G[to][G[u][i].rev].cab += d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s, int t){
    int ret = 0, tmp;
    while(true){
        memset(level, -1, sizeof level);
        bfs(s, t);
        if (level[t] == -1) return ret;
        memset(iter, 0, sizeof iter);
        while((tmp = dfs(s, t, inf)) != 0) ret += tmp;
    }
}
int solve(){
	memset(used, false, sizeof used);
	int sum = 0;
	for (int i = 1; i <= tot; ++i){
//		cout << i << ' ' << "list: = " << endl;
		for (int j = 0; j < E[i].size(); ++j){
			add(E[i][j].first, E[i][j].second, 1);
			if (!used[E[i][j].second]) add(E[i][j].second, Tmp+1, 1);
			used[E[i][j].second] = true;
//			ppr(E[i][j].first, E[i][j].second);
		}
//		cout << max_flow(1, Tmp+1) << endl;
		sum += max_flow(1, Tmp+1);
//		ppr(i, sum);
//		pr(sum);
		if (sum == tot) return i;
	}
	return -1;
}
int main(){
//必须编译过才能交 
//	ios::sync_with_stdio(false);
	int ik, i, j, k, kase;
	scanf("%d", &kase);
	while(kase--){
		scanf("%d%d", &n, &m);
		for (i = 1; i <= n; ++i) scanf("%s", mp[i]+1);
		tot = 0;
		for (i = 1; i < 5100; ++i) G[i].clear();
		for (i = 1; i <= n; ++i)
			for (j = 1; j <= m; ++j) if (mp[i][j] == '.'){
				++tot;
				add(1, tot+1, 1);
				ord[i][j] = tot+1;
			}
		Tmp = tot + 1;
		for (i = 1; i <= tot; ++i) E[i].clear();
		for (i = 1; i <= n; ++i)
			for (j = 1; j <= m; ++j)
				if (mp[i][j] == 'D'){
					bfs(i, j, Tmp);
					Tmp += tot; 
				}
//		for (j = 1; j < tot+1; ++j) cout << E[j].size() << endl; 
		int ans = solve();
		if (ans == -1) puts("impossible");
		else printf("%d\n", ans); 
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值