D. Cross the Maze (网络流)

题目

https://codeforces.com/gym/103427/problem/D
在这里插入图片描述

大意:给一个 a*b 的地图,有 n 个人和 n 个出口。出口只能用一次,人到了出口可以选择不出去。每次人可以LRUDS(不动)P(在出口或已经出去了才有这个,代表出去)五种操作(都是瞬移)。每个时刻每个位置只能有一个人。问 n 个人全出去的最少时间以及给出一种方案。

思路

这是官方题解 https://www.zhihu.com/question/500358591/answer/2239217951
注意数据都是100级别的。
首先,肯定有解,且不大于 a*b。因为可以把地图按贪吃蛇那样展开成一条线,然后一次分配出口即可。
那么,就可以先二分答案 t,再建造一个 t 层的图跑网络流。
在这里插入图片描述
如图,是个 t=3 的情形
每层有两组,上面那组每对点代表一个格子,下面那组每对点代表一个出口。之所以每层的点要成对出现,是为了各自在他们之间连上一条边权1的边来限制住流量。
跑完网络流,每条流量代表一个人的路径。最终流量刚好为 n 则代表可以成功。
时间复杂度是二分乘上网络流。

代码

// https://codeforces.com/gym/103427/problem/D
#include <bits/stdc++.h>
using namespace std;


template <typename Cap = int64_t>
class Dinic{
private:
	struct E{
		int to, rev;
		Cap cap;
        char ch;
	};
	int n, st, ed;
	vector<vector<E>> G;
	vector<int> lv, idx;
	vector<pair<int, int>> egs;
    
	bool BFS(){
		lv.assign(n, -1);
		queue<int> bfs;
		bfs.push(st); lv[st] = 0;
		while (not bfs.empty()){
			int u = bfs.front(); bfs.pop();
			for (auto e: G[u]) {
				if (e.cap <= 0 or lv[e.to]!=-1) continue;
				bfs.push(e.to); lv[e.to] = lv[u] + 1;
			}
		}
		return lv[ed] != -1;
	}
	Cap DFS(int u, Cap f){
		if (u == ed) return f;
		Cap ret = 0;
		for(int &i = idx[u]; i < int(G[u].size()); ++i) {
			auto &e = G[u][i];
			if (e.cap <= 0 or lv[e.to]!=lv[u]+1) continue;
			Cap nf = DFS(e.to, min(f, e.cap));
			ret += nf; e.cap -= nf; f -= nf;
			G[e.to][e.rev].cap += nf;
			if (f == 0) return ret;
		}
		if (ret == 0) lv[u] = -1;
		return ret;
	}
public:
	void init(int n_) { G.assign(n = n_, vector<E>()); egs.clear(); }
	void addEdge(int u, int v, Cap c, char ch){
		G[u].push_back({v, int(G[v].size()), c, ch});
		G[v].push_back({u, int(G[u].size())-1, 0, ch});
		egs.emplace_back(v, int(G[v].size()) - 1);
	}
	Cap maxFlow(int st_, int ed_){
		st = st_, ed = ed_; Cap ret = 0;
		while (BFS()) {
			idx.assign(n, 0);
			Cap f = DFS(st, numeric_limits<Cap>::max());
			ret += f;
			if (f == 0) break;
		}
		return ret;
	}
	Cap flow_at(int eid) const {
		return G[egs[eid].first][egs[eid].second].cap;
	}

    // 输出答案
    void printAns(int Ha, int AB, int b, int t)
    {
        for (int i=0; i<int(G[st].size()); i++) {
            int cur=G[st][i].to;
            int lst=ed;
            int x,y,xx,yy;
            x=xx=(cur-1)%Ha/2/b;
            y=yy=(cur-1)%Ha/2%b+1;
            string str;
            while (cur!=ed) {
                for (int j=0; j<int(G[cur].size()); j++) {
                    E &e=G[cur][j];
                    if (!e.cap && e.to!=lst && (e.to-1)/Ha>=(cur-1)/Ha) {
                        if (e.ch>='A' && e.ch<='Z') str+=e.ch;
                        //printf("%c",Ech[h]);
                        lst=cur;
                        cur=e.to;
                        if (cur%Ha<=AB && (cur%2==0) && cur!=ed) {
                            xx=(cur-1)%Ha/2/b;
                            yy=(cur-1)%Ha/2%b+1;
                        }
                        break;
                    }
                }
            }
            str.pop_back();
            printf("%d %d %d %d %s\n",x,y,xx,yy,str.c_str());
        }
    }
};



int n,a,b;
int S,T;
int SUM,AB;
int sx[200],sy[200],ex[200],ey[200];
int fx[4][2]={{-1,0}, {1,0}, {0,-1}, {0,1}};
char ch[4]={'U','D','L','R'};
Dinic<int> DDD;
int trans_s2m(int x, int y, int t) {return (x*b+y)*2+SUM*t;}
int trans_e2m(int x, int t) {return x*2+AB+SUM*t;}



bool check(int ttt)
{
    S=0;
    T=(ttt+1)*SUM;
    DDD.init(T+10);

    for (int i=1; i<=n; i++) {
        DDD.addEdge(S, trans_s2m(sx[i], sy[i], 0), 1, '.');
    }

    for (int t=0; t<ttt; t++) {
        for (int i=1; i<=a; i++)
            for (int j=1; j<=b; j++) {
                DDD.addEdge(trans_s2m(i,j,t), trans_s2m(i,j,t)+1, 1, '-');
                DDD.addEdge(trans_s2m(i,j,t)+1, trans_s2m(i,j,t+1), 1, 'S');
                for (int ff=0,ii,jj; ff<4; ff++) {
                    ii=i+fx[ff][0], jj=j+fx[ff][1];
                    if (ii<=0 || ii>a || jj<=0 || jj>b) continue;
                    DDD.addEdge(trans_s2m(i,j,t)+1, trans_s2m(ii,jj,t+1), 1, ch[ff]);
                }
                for (int k=1; k<=n; k++) {
                    DDD.addEdge(trans_e2m(k,t), trans_e2m(k,t)+1, 1, '-');
                    DDD.addEdge(trans_e2m(k,t)+1, trans_e2m(k,t+1), 1, 'P');
                    DDD.addEdge(trans_s2m(ex[k],ey[k],t)+1, trans_e2m(k,t+1), 1, 'P');
                }
            }
    }

    for (int i=1; i<=n; i++) {
        DDD.addEdge(trans_e2m(i,ttt), T, 1, '!');
    }

    int mxflow = DDD.maxFlow(S, T);
    return mxflow==n;
}


void solve()
{
    scanf("%d%d%d",&n,&a,&b);
    AB=(a+1)*b*2;
    SUM=AB+n*2+2;

    vector<int> s(n+1);
    for (int i=1; i<=n; i++) {
        scanf("%d%d",&sx[i],&sy[i]);
    }
    for (int i=1; i<=n; i++) {
        scanf("%d%d", &ex[i], &ey[i]);
    }

    int ans=a*b;
    int L=0, R=a*b;
    while (L<=R) {
        int M=(L+R)>>1;
        if (check(M)) {
            ans=M;
            R=M-1;
        }
        else {
            L=M+1;
        }
    }

    check(ans);
    printf("%d\n",ans-1);
    DDD.printAns(SUM, AB, b, ans-1);

}


int main()
{
    solve();
    return 0;
}

/*
// 3 4 4 1 1 1 4 4 4 1 3 2 3 2 4
// 3 2 2 1 1 1 2 2 2 1 1 2 1 2 2
// 2 3 3 1 1 1 3 1 2 2 2

10 1 100 1 17 1 49 1 12 1 37 1 83 1 44 1 75 1 78 1 72 1 3 1 75 1 47 1 55 1 81 1 6 1 59 1 17 1 68 1 28 1 24 

10 1 100
1 17
1 49
1 12
1 37
1 83
1 44
1 75
1 78
1 72
1 3
1 75
1 47
1 55
1 81
1 6
1 59
1 17
1 68
1 28
1 24


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值