1. 程式人生 > >Codeforces 513F2 題解 (網路流-最大流 二分 BFS)

Codeforces 513F2 題解 (網路流-最大流 二分 BFS)

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 debug

const 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;
}