【BZOJ 1189】[HNOI2007]紧急疏散evacuate

【链接】 我是链接,点我呀:)
【题意】

在这里输入题意

【题解】

二分+网络流

先处理出所有的人到所有的门的最短路dis(x,y)

二分所用的时间mid
则把所有的门都分成mid个。
表示1..mid这些时间这个门可以通过一个人。
然后建立一个超级源点S
S和所有的人各连一条边,边权为1
然后如果人x能到达门y的话
那么从x连mid-dis[x,y]+1条边分别至门的第dis[x,y]个点,第dix[x,y]+1个点....第mid个点(每个点各一条,刚好mid-dis[x][y]+1条。
表示这些时间段这个人都能到达这个点。
然后所有的门都向超级汇点连一条边。
这样可以保证,每个门,每个时刻只有一个人经过
每次跑个最大流
看看是不是满流就好。
(即最大流等于人的个数

是的话mid尝试减小一点

【代码】

#include <bits/stdc++.h>
using namespace std;

const int N = 20;
const int NN = 400;
const int NNN = 160000;
const int INF = 0x3f3f3f3f;
const int dx[4] = {0,0,1,-1};
const int dy[4] = {1,-1,0,0};

struct abc{
    int from,en,cost,nex;
};

int n,m,cnt = 1,what[N+10][N+10],now = 0;
int g[NN+10][NN+10],dis[N+10][N+10],dis1[NNN+NN+10],fir[NNN+NN+10],mi[NNN+NN+10],pre[NNN+NN+10];
char s[N+10][N+10];
bool inq[NNN+10];
vector<abc> edge;
queue<pair<int,int> > dl;
queue<int> dl2;

void add_edge(int x,int y,int cost){
    abc temp;
    temp.from = x,temp.en = y,temp.cost = cost,temp.nex = fir[x];
    edge.push_back(temp);
    int tot = edge.size()-1;
    fir[x] = tot;
}

void bfs(int cur,int x,int y){
    memset(dis,INF,sizeof dis);
    dis[x][y] = 0;
    dl.push(make_pair(x,y));
    while (!dl.empty()){
        pair<int,int> temp = dl.front();
        int xx = temp.first,yy = temp.second;
        dl.pop();
        for (int i = 0;i < 4;i++){
            int tx = xx+dx[i],ty = yy+dy[i];
            if (tx>=1 && tx<=n && ty>=1 && ty<=m){
                if (s[tx][ty]=='X' || s[tx][ty]=='D') continue;
                if (dis[tx][ty]>dis[xx][yy]+1){
                    dis[tx][ty] = dis[xx][yy]+1;
                    dl.push(make_pair(tx,ty));
                    g[what[tx][ty]][cur] = dis[tx][ty];
                }
            }
        }
    }
}

bool ok(int t){
    memset(fir,255,sizeof fir);edge.clear();
    for (int i = 2;i <= cnt;i++) {
        add_edge(1,i,1);
        add_edge(i,1,0);
    }
    int cur = cnt,tnow = 0;
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++)
            if (s[i][j]=='D'){
                tnow++;
                int pp = cur;
                for (int k = 1;k <= t;k++) cur++;
                for (int k = 2;k <= cnt;k++)
                    if (g[k][tnow]<INF){
                        for (int l = pp+g[k][tnow];l <= cur;l++) {
                            add_edge(k,l,1);
                            add_edge(l,k,0);
                        }
                    }
            }
    cur++;
    for (int i = cnt+1;i <= cur-1;i++) {
            add_edge(i,cur,1);
            add_edge(cur,i,0);
    }

    int flow = 0;
    while (1){
        int s = 1,t = cur;
        dis1[s] = 0,inq[s] = 1;
        dl2.push(s);
        memset(pre,255,sizeof pre);
        while (!dl2.empty()){
            int x = dl2.front();
            dl2.pop();
            for (int i = fir[x];i!=-1;i = edge[i].nex){
                int y = edge[i].en;
                if (edge[i].cost>0 && pre[y]==-1){
                    pre[y] = i;
                    dl2.push(y);
                }
            }
        }
        if (pre[t]==-1) break;
        int now = t,temp1 = INF;
        while (now != s){
            int temp = pre[now];
            temp1 = min(temp1,edge[temp].cost);
            now = edge[temp].from;
        }
        now = t;
        while (now!=s){
            int temp = pre[now];
            edge[temp].cost-=temp1;
            edge[temp^1].cost+=temp1;
            now = edge[temp].from;
        }
        flow+=now;
    }
    if (flow==cnt-1) return true;
    else return false;
}

int main(){
    //freopen("D:\\rush.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= n;i++) scanf("%s",s[i]+1);
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++)
            if (s[i][j]=='.'){
                cnt++;
                what[i][j] = cnt;
            }

    memset(g,INF,sizeof g);
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= m;j++)
            if (s[i][j]=='D'){
                bfs(++now,i,j);
            }


    int l = 1,r = n*m,temp = -1;
    while (l <= r){
        int m = (l+r)>>1;
        if (ok(m)){
            temp = m;
            r = m - 1;
        }else l = m + 1;
    }
    if (temp==-1){
        printf("impossible\n");
    }else{
        printf("%d\n",temp);
    }
    return 0;
}

posted @ 2018-04-04 11:00  AWCXV  阅读(190)  评论(0编辑  收藏  举报