ECNA 2013 Stampede! (最大流)

Problem E: Stampede!
You have an n×n game board. Some squares contain obstacles, except the left- and right-most columns
which are obstacle-free. The left-most column is filled with your n pieces, 1 per row. Your goal is to
move all your pieces to the right-most column as quickly as possible. In a given turn, you can move
each piece N, S, E, or W one space, or leave that piece in place. A piece cannot move onto a square
containing an obstacle, nor may two pieces move to the same square on the same turn. All pieces move
simultaneously, so one may move to a location currently occupied by another piece so long as that piece
itself moves elsewhere at the same time.
Given n and the obstacles, determine the fewest number of turns needed to get all your pieces to the
right-hand side of the board.
Input
Each test case starts with a positive integer n indicating the size of the game board, with n ≤ 25.
Following this will be n lines containing n characters each. If the j
th
character in the i
th
line is an ‘X’,
then there is an obstacle in board location i, j; otherwise this character will be a ‘.’ indicating no
obstacle. There will never be an obstacle in the 0
th
or (n − 1)
st
column and there will always be at least
one obstacle-free path between these two columns. A line containing a single 0 will terminate input.
Output
For each test case output the minimum number of turns to move all the pieces from the left side of the
board to the right side.
Sample Input
5
.....
.X...
...X.
..X..
.....
5
.X...
.X...
.X...
.XXX.
.....
0
Sample Output
Case 1: 6

Case 2: 8


开始假设这题也是类似推箱子的问题是无障碍移动,先bfs求最短路,然后二分用最大匹配判合法,wa了。然后找了组数据反驳了这个假设。立马想到了根据时间拆点建图,二分最大时间limit,每个点就拆成了limit+1个,源点向左边一列时间为0的点连边,右边一列时间为limit的点向汇点连边,注意每条边的容量都是1,然后跑最大流判合法性。

#include<cstdio>
#include<map>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
#include<set>
#include<cmath>
using namespace std;
const int maxn = 1e3 + 5;
const int INF = 1e9;
const double eps = 1e-6;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> P;
#define fi first
#define se second

struct Edge{
    int from, to, cap, flow;
};

struct Dinic{
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[maxn*maxn];
    bool vis[maxn*maxn];
    int d[maxn*maxn];
    int cur[maxn*maxn];

    void AddEdge(int from, int to, int cap){
        edges.push_back((Edge){from, to, cap, 0});
        edges.push_back((Edge){to, from, 0, 0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    void init(int n){
        for(int i = 0;i < n;i++) G[i].clear();
        edges.clear();
    }
    bool BFS(){
        memset(vis, 0, sizeof vis);
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!Q.empty()){
            int x = Q.front();Q.pop();
            for(int i = 0;i < G[x].size();i++){
                Edge& e = edges[G[x][i]];
                if(!vis[e.to] && e.cap > e.flow){
                    vis[e.to] = 1;
                    d[e.to] = d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }

    int DFS(int x, int a){
        if(x == t || a == 0)
            return a;
        int flow = 0, f;
        for(int& i = cur[x];i < G[x].size();i++){
            Edge& e = edges[G[x][i]];
            if(d[x]+1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0){
                e.flow += f;
                edges[G[x][i]^1].flow -= f;
                flow += f;
                a -= f;
                if(a == 0) break;
            }
        }
        return flow;
    }

    int Maxflow(int s, int t){
        this -> s = s;
        this -> t = t;
        int flow = 0;
        while(BFS()){
            memset(cur, 0, sizeof cur);
            flow += DFS(s, INF);
        }
        return flow;
    }
};

Dinic solver;

char maze[maxn][maxn];
int n;
int id(int x, int y, int ceng){
    return x*n+y+1+ceng*n*n;
}
const int cx[] = {-1, 0, 1, 0};
const int cy[] = {0, 1, 0, -1};

bool test(int limit){
    solver.init(n*n*(limit+1)+5);
    int source = 0, sink = n*n*(limit+1)+1;

    for(int i = 0;i < n;i++){
        solver.AddEdge(source, id(i, 0, 0), 1);
        solver.AddEdge(id(i, n-1, limit), sink, 1);
    }
    for(int i = 0;i < n;i++){
        for(int j = 0;j < n;j++){
            if(maze[i][j]=='.'){
                for(int k = 0;k < limit;k++){
                    solver.AddEdge(id(i, j, k), id(i, j, k+1), 1);
                    for(int dir = 0;dir < 4;dir++){
                        int tx = i+cx[dir];
                        int ty = j+cy[dir];
                        if(tx>=0 && tx<n && ty>=0 && ty<n && maze[tx][ty]=='.'){
                            solver.AddEdge(id(i, j, k), id(tx, ty, k+1), 1);
                        }
                    }
                }
            }
        }
    }
    if(solver.Maxflow(source, sink)==n)
        return true;
    return false;
}

int main(){
    int kase = 0;
    while(cin >> n){
        if(n == 0)
            break;
        kase++;
        for(int i = 0;i < n;i++)
            cin >> maze[i];
        int l = 0, r = n*n+10, ans;
        while(l <= r){
            int mid = (l+r)/2;
            if(test(mid)){
                ans = mid;
                r = mid-1;
            }
            else
                l = mid+1;
        }
        printf("Case %d: %d\n", kase, ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值