Codeforces 513F2 题解 (网络流-最大流 二分 BFS)

来源:互联网 发布:知乎提问怎么取消匿名 编辑:程序博客网 时间:2024/06/16 16:31

Scaygerboss


题目描述

在一个有障碍的网格图中,有male 个男人和female 个女人,还有一个叫BOSS的人妖(既可以当男人又可以当女人)。这些人分布在地图上,每一个cell可以同时有多个人。这些人每个人移动各需要ti 的时间,问最小需要多长时间,对于每一个人都可以和异性单独待在同一个房间里?

Sample Input

说明:

在n*m的地图上,‘.’表示一个free room ,既可以移动到或者经过的房间;‘#’表示一个障碍物单位,既不可达也不可经过。

第一行为四个整数,n,m,male,female ,分别表示地图的行数,列数,男性和女性的个数。
接下来n 行描述一个地图,表示是否可达。
接下来1行描述BOSS(人妖),x,y,t ,表示这个人所在的坐标和移动一个单位所需的时间。
接下来的male 行和female 行分别描述男人和女人,格式同上。

4 4 2 3
….
.###
####
####
2 1 1
2 1 2
2 1 2
2 1 2
2 1 2
1 1 2

Sample Output

说明:

一行一个整数,表示最少的所需时间。

2

Hint

Consider the first sample test. The scaygers are hiding on a 4 by 4 map. Scaygerboss initially resides in the cell (2, 1) and can move between cells in 1 unit of time. There are also 2 male and 3 female scaygers on the map. One of the females initially is in the cell (1, 1), and all the other scaygers are in the cell (2, 1). All the scaygers move between cells in 2 units of time. If Scaygerboss and the female scayger from the cell (1, 1) move to the cell (1, 2), and a male and a female scayger from those residing in the cell (2, 1) move to the cell (1, 1), then all the scaygers will look nerdy and lovable in 2 units of time.

———(其实就是描述描述两个单位时间怎么行动,不译了)———

  • In subproblem F1 (14 points), the constraints 1 ≤ n, m ≤ 11 will hold.
  • In subproblem F2 (6 points), the constraints 1 ≤ n, m ≤ 22 will hold.

题解

通过阅读题意可以得知,要求得最小的达到目标状态的时间取决于最后一个到位的人的时间,所以这就是一个求出最小的最大值的问题。

并且我们也可以看出,每个房间可以同时存在多个人,所以就可以看出这个问题的决策具有单调性:如果某一刻能达到目标状态,那么下一刻一定也能维持目标状态,如果某一刻不能达到目标状态,那么上一刻也不能达到目标状态,所以可以使用二分法。

考虑到二分答案,我们首先要思考的就是check函数如何编写。在check函数的检验中我们就已经假设了要在val(mid)时间内可达到的状态。那么回到问题来看,对于每一个人来说只要和异性之一能够在同一个格子里面相遇且每一对相遇都在不同的格子里就可以了。也就是说,必须每个人都和一个异性配对且每个格子只能有一格子对。

那么问题就转化为了网络流问题!

每一个人能和异性相遇,就可以转化为从这个人向异性流过去流量为1的流;每个格子里只有一对,就可以用拆点的方法让每一个格子的流量上界为1。对于每一个人,就应该向他可以在val时间内走到的点连一条流量为1的边。

所以说建图的模型已经显而易见了:从源点向所有的male连一条流量为1的边,从male向可达的所有点连一条流量为1的边,一个点拆出的两个点之间的流量为1,所有female可达的点向female连一条流量为1的边,最后由所有的female连向汇点。

为了保证时间复杂度,在二分之前应该从每一个人作为起点开始bfs,处理出对于每一个点的距离,如果某个人到点的距离*这个人移动一单位的时间<=mid,就把这条边加进去。

注意:
对于INF的处理,如果INF设得比较大一定要特殊判断#障碍点。因为t很大,乘上一个INF很有可能爆int变成负数导致答案变小。这个地方我错了一次要注意一点。

还有一点就是答案的上界超过了int,记得二分的时候用longlong。


代码

先粘为快!

#include <cstdio>#include <iostream>#include <cmath>#include <algorithm>#include <queue>using namespace std;//#define debugconst int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};const int maxn=25;const int INF=int(1e9)+7;int n,m,male,female;char Map[maxn][maxn];struct Node {    int x,y;    int speed;    long long dis[maxn][maxn];    bool used[maxn][maxn];    queue<int> quex,quey;    void read() {        scanf("%d%d%d",&x,&y,&speed);        return;    }    void pre_bfs() {        for(int i=1;i<=n;i++)            for(int j=1;j<=m;j++)                dis[i][j]=INF,used[i][j]=false;        while(quex.size()) quex.pop();        while(quey.size()) quey.pop();        dis[x][y]=0;        used[x][y]=true;        quex.push(x),quey.push(y);        while(quex.size()) {            int a=quex.front(),b=quey.front(); quex.pop(),quey.pop();            used[a][b]=false;            for(int k=0;k<4;k++) {                int nx=a+dir[k][0],ny=b+dir[k][1];                if(Map[nx][ny]=='#') continue;                if(nx<1 || ny<1 || nx>n || ny>m || dis[nx][ny]<=dis[a][b]+1) continue;                dis[nx][ny]=dis[a][b]+1;                if(!used[nx][ny]) {                    used[nx][ny]=true;                    quex.push(nx), quey.push(ny);                }            }        }        return;    }}node1[maxn*maxn], node2[maxn*maxn];void read() {    scanf("%d%d%d%d",&n,&m,&male,&female);    for(int i=1;i<=n;i++) scanf("%s",Map[i]+1);    Node boss;    boss.read();    for(int i=1;i<=male;i++) node1[i].read();    for(int i=1;i<=female;i++) node2[i].read();    if(male-female==1) node2[++female]=boss;    else if(female-male==1) node1[++male]=boss;    else {        printf("-1\n");        exit(0);    }    return;}void init() {    for(int i=1;i<=male;i++) node1[i].pre_bfs();    for(int i=1;i<=female;i++) node2[i].pre_bfs();    return;}const int maxd=int(1e4)+7;const int maxe=int(1e6)+7;int tot=0;int head[maxd];int S,T;#define perm(k) (S+k)#define perf(k) (perm(male)+k)#define posin(i,j) (perf(female)+(i-1)*m+j)#define posout(i,j) (posin(n,m)+(i-1)*m+j)struct Edge {    int from,to,cap,next;    Edge() {}    Edge(int x,int y,int c,int nx):from(x),to(y),cap(c),next(nx) {}}eage[maxe*2];void add(int x,int y,int c) {    eage[tot]=Edge(x,y,c,head[x]), head[x]=tot++;    eage[tot]=Edge(y,x,0,head[y]), head[y]=tot++;    return;}int dis[maxd];bool used[maxd];queue<int> que;bool bfs() {    for(int i=S;i<=T;i++) dis[i]=INF,used[i]=false;    while(que.size()) que.pop();    que.push(S);    used[S]=true;    dis[S]=0;    while(que.size()) {        int u=que.front(); que.pop();        used[u]=false;        for(int i=head[u];~i;i=eage[i].next) if(dis[eage[i].to]>dis[u]+1 && eage[i].cap) {            int v=eage[i].to;            dis[v]=dis[u]+1;            if(!used[v]) {                used[v]=true;                que.push(v);            }        }    }    return (dis[T]<INF);}int dfs(int u,int flow) {    if(u==T || !flow) return flow;    int ret=0;    for(int i=head[u];~i;i=eage[i].next) if(eage[i].cap && dis[eage[i].to]==dis[u]+1) {        int v=eage[i].to;        int newf=dfs(v,min(flow,eage[i].cap));        eage[i].cap-=newf;        eage[i^1].cap+=newf;        ret+=newf;        flow-=newf;        if(!flow) break;    }    if(!ret) dis[u]=-1;    return ret;}int dinic() {    int res=0;    while(bfs()) res+=dfs(S,INF);    return res;}void build(long long val) {    S=1,T=posout(n,m)+1;    for(int i=S;i<=T;i++) head[i]=-1;    tot=0;    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)            add(posin(i,j),posout(i,j),1);    for(int i=1;i<=male;i++) {        add(S,perm(i),1);        for(int x=1;x<=n;x++)            for(int y=1;y<=m;y++) if(node1[i].dis[x][y]!=INF && node1[i].dis[x][y]*node1[i].speed<=val)                add(perm(i),posin(x,y),1);    }    for(int i=1;i<=female;i++) {        add(perf(i),T,1);        for(int x=1;x<=n;x++)            for(int y=1;y<=m;y++) if(node2[i].dis[x][y]!=INF && node2[i].dis[x][y]*node2[i].speed<=val)                add(posout(x,y),perf(i),1);    }    return;}bool check(long long val) {    build(val);    return (dinic()==male);}int main() {    read();    init();    long long l=0,r=(1ll<<40),res=-1;    while(l<=r) {        long long mid=(l+r)>>1;        if(check(mid)) r=mid-1,res=mid;        else l=mid+1;    }    printf("%I64d\n",res==INF?-1:res);    return 0;}
1 0
原创粉丝点击