绝世污题,垃圾题,浪费我一整天青春!

1189: [HNOI2007]紧急疏散evacuate

Time Limit: 10 Sec Memory Limit: 162 MB

Submit: 1262 Solved: 464

[Submit][Status][Discuss]

Description

发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是’.’,那么表示这是一块空地;如果是’X’,那么表示这是一面墙,如果是’D’,那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。

Input

输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符’.’、’X’和’D’,且字符间无空格。

Output

只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出’impossible’(不包括引号)。

Sample Input

5 5

XXXXX

X…D

XX.XX

X..XX

XXDXX

Sample Output

3

HINT

Source

这道题绝对污的不行,第一次尝试自己理想建图,崩烂;
第二次修改时走投无路上网看解析,在评论的指引下发现网上神犇学长的建图都忽略了**每单位时间每个门只能走一个人**这一点,实现后发现数据水,能过
但是在与YveH爷和HZWer学长的交谈中对正确建模有了想法;
第三次尝试正解,又搞了半天╮(╯▽╰)╭最后10组数据,9组一共跑了不到1s,极限数据一组4.5s。。(还好bzoj测总时间)

建图如下(神级建图):

1.BFS预处理出每个’.’到每个’D’的曼哈顿距离(最短移动时间)

2.二分最短总时间,mint

3.把每个’D’按时间拆点,即把每个’D’都拆成mint个点

4.判断这个’.’到这个’D’的时间是否<=mint,满足则将这个’.’与拆后的’D’的点连边(从这个’.’到’D’的所需时间到拆成的最后一个点都要连边)边权为1 ;

PS:这里的意思就是二分出的mint,把每个’D’都拆成mint个点,假如这个’.’到这个’D’的时间为t,且t

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int dis[1100000];
struct data{
int next,to,v;
}edge[1100100];
int head[1000100],cnt;
int n,m;
int rnum=0,dnum=0;
int q[200010],h,t;
int mt[410][25][25]={0};
struct data1{
int x,y,step;
};
bool can[25][25]={0};
bool visit[25][25]={false};
int move[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
int cs[25][25]={0};
int num; void add(int u,int v,int w)
{
cnt++;
edge[cnt].v=w;
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
} void insert(int u,int v,int w)
{
add(u,v,w);add(v,u,0);
} void sear(int nowx,int nowy)
{
queue<data1> que;
int p=-cs[nowx][nowy];//cs【】【】里门的序号是存的负数。。
que.push({nowx,nowy,0});
while (!que.empty())
{
data1 temp=que.front();
que.pop();
int x=temp.x;
int y=temp.y;
int deep=temp.step;
visit[x][y]=true;
for (int i=0;i<4;i++)
if (!visit[x+move[i][0]][y+move[i][1]] && cs[x+move[i][0]][y+move[i][1]]>0 &&
x+move[i][0]>=1 && x+move[i][0]<=n && y+move[i][1]>=1 && y+move[i][1]<=m)
que.push({x+move[i][0],y+move[i][1],deep+1});
mt[p][x][y]=deep;
can[x][y]=1;//can【】【】记录这个点能不能走到门
}
}//预处理出每个'.'到每个'D'的距离 void init()
{
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
{
char x[25];
scanf("%s",&x);
for (int j=1; j<=m; j++)
{
if (x[j-1]=='.') {rnum++;cs[i][j]=rnum;}
if (x[j-1]=='X') cs[i][j]=0;
if (x[j-1]=='D') {dnum++;cs[i][j]=-dnum;}
}
}//rnum表示人数,dnum表示门数(总之可以理解为>0的是人,=0的是墙,<0的是门)
memset(mt,0x3f,sizeof(mt));
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
if (cs[i][j]<0)
{
memset(visit,false,sizeof(visit));
sear(i,j);
}//遇到门就BFS出能到这个门的各个点的距离
} void make(int mint)
{
cnt=1;memset(head,0,sizeof(head));
for (int i=1; i<=dnum; i++)
for (int j=1; j<=n; j++)
for (int k=1; k<=m; k++)
if (cs[j][k]>0 && mt[i][j][k]<=mint)
for (int l=mt[i][j][k]; l<=mint; l++)
insert((j-1)*m+k,n*m*i+l,1);//点到拆过后的门的连边
for (int i=1; i<=dnum; i++)
for (int j=1; j<=mint; j++)
insert(n*m*i+j,n*m*(dnum+1)+1,1);//拆过后的门到超级汇连边
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
if (can[i][j] && cs[i][j]>0)
insert(0,(i-1)*m+j,1);//超级源到个点连边
num=n*m*(dnum+1)+1;
} bool bfs()
{
memset(dis,-1,sizeof(dis));
q[1]=0; dis[0]=1;
h=0;t=1;
while (h<t)
{
int j=q[++h],i=head[j];
while (i)
{
if (edge[i].v>0 && dis[edge[i].to]<0)
{
dis[edge[i].to]=dis[j]+1;
q[++t]=edge[i].to;
}
i=edge[i].next;
}
}
if (dis[num]>0)
return true;
else
return false;
} int dfs(int loc,int low)
{
int i=head[loc];
if (loc==num) return low;
while (i)
{
int flow=0;
if (edge[i].v>0 && dis[edge[i].to]==dis[loc]+1 && (flow=dfs(edge[i].to,min(edge[i].v,low))))
{
edge[i].v-=flow;
edge[i^1].v+=flow;
return flow;
}
i=edge[i].next;
}
return 0;
} int main()
{
init();
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
if (cs[i][j]>0 && !can[i][j]) {printf("impossible");return 0;}//如果有人不能走到门则impossible
int left=0,right=n*m;
while (left<=right)
{
int mid=(left+right)>>1;
make(mid);
int ans=0;
while (bfs())
{
int now;
while ((now=dfs(0,0x7fffffff)))
ans+=now;
}
if (ans<rnum) left=mid+1;
else right=mid-1;
}
printf("%d\n",left);
return 0;
}

BZOJ-1189 紧急疏散evacuate BFS预处理+最大流+二分判定+神建模!!的更多相关文章

  1. BZOJ 1189: [HNOI2007]紧急疏散evacuate( BFS + 二分答案 + 匈牙利 )

    我们可以BFS出每个出口到每个人的最短距离, 然后二分答案, 假设当前答案为m, 把一个出口拆成m个表示m个时间, 点u到出口v的距离为d, 那么u->v的[d, m]所有点连边, 然后跑匈牙利 ...

  2. 洛谷 P3191 [HNOI2007]紧急疏散EVACUATE(网络最大流)

    题解 二分答案+Dinic最大流 二分答案\(mid\) 把门拆成\(mid\)个时间点的门 相邻时间的门连一条\(inf\)的边 预处理出每个门到每个人的最短时间 为\(dis[k][i][j]\) ...

  3. bzoj 1189: [HNOI2007]紧急疏散evacuate 分层图最大流_拆点_二分

    Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一 块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是 ...

  4. BZOJ 1189 紧急疏散(二分+最大流)

    求出所有人撤离的最短时间.由于每扇门只能通过一次,所以不能简单用bfs来搞. 显然答案是有单调性的,考虑二分,问题变成了判断时间x所有人能不能撤离. 考虑最大流.对于每扇门,每个时间通过的人数最多为1 ...

  5. 【bzoj1189】[HNOI2007]紧急疏散evacuate BFS最短路+动态加边网络流

    题目描述 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以 ...

  6. bzoj 1189 紧急疏散 网络流

    二分答案,网络流判断 将每个门拆点,每个人连向每个门的dis~当前解 然后跑最大流,如果等于人数,即为可行解 #include<cstdio> #include<iostream&g ...

  7. BZOJ 2756: [SCOI2012]奇怪的游戏 [最大流 二分]

    2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 3352  Solved: 919[Submit][Stat ...

  8. BZOJ-3130 费用流 (听题目胡扯丶裸最大流) 二分判定+最大流+实数精度乱搞

    DCrusher爷喜欢A我做的水题,没办法,只能A他做不动的题了.... 3130: [Sdoi2013]费用流 Time Limit: 10 Sec Memory Limit: 128 MBSec ...

  9. BZOJ-1305 dance跳舞 建图+最大流+二分判定

    跟随YveH的脚步又做了道网络流...%%% 1305: [CQOI2009]dance跳舞 Time Limit: 5 Sec Memory Limit: 162 MB Submit: 2119 S ...

随机推荐

  1. GitHub Top 100的Android开源库

    摘要: 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据GitHub搜索Java语言选择「Best M... 本项目主要对目前 GitH ...

  2. Ubuntu安装JDK与配置环境变量

    Ubuntu14.04安装JDK与配置环境变量 工具/原料   Ubuntu14.04系统 方法/步骤     先从Oracle官网下载JDK.先选择同意按钮,然后根据自己的系统下载相应版本.我的系统 ...

  3. fireworks将图片变为透明色

    如果是新建的图片,只要把画布背景设置成透明,图片完成后保存为GIF格式即可: 如果是已经存在的图片,用Fireworks将图片打开,然后按Ctrl+Shift+X,在弹出界面中格式选择为GIF.在右边 ...

  4. js学习推荐

    1.汤姆大叔 http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html

  5. vim常用命令汇总

    vim常用命令汇总: http://www.cnblogs.com/softwaretesting/archive/2011/07/12/2104435.html 定位 本行第一个字符 ctrl+$ ...

  6. django字段设置null和blank的区别

    null 这个选项跟数据库有关. null=True的话,数据库中该字段是NULL,即允许空值:null=False(默认)的话,数据库中该字段是NOT NULL,即不允许空值. blank 这个选项 ...

  7. 《图解tcp/ip》读书笔记(二)

    <图解tcp/ip>读书笔记(二) 本周主要阅读的是本书的第三章--数据链路. 当然了,从某些角度讲,我认为这一章就是计算机网络的最基本的内容之一.整章讲述了数据链路层的作用和相关技术,主 ...

  8. Scala学习笔记(五):类和对象

    对象实例的变量组成了对象的内存映射 public是Scala的默认访问级别 Scala里方法参数的一个重要特征是它们都是val,不是var Scala不能定义静态成员 单例对象(singleton o ...

  9. python&MongoDB爬取图书馆借阅记录(没有验证码)

    题外话:这个爬虫本来是想用java完成然后发布在博客园里的,但是一直用java都失败了,最后看到别人用了python,然后自己就找别人问了问关键的知识点,发现连接那部分,python只用了19行!!! ...

  10. 7天学会spring cloud教程

    按照官方的话说:Spring Cloud 为开发者提供了在分布式系统(如配置管理.服务发现.断路器.智能路由.微代理.控制总线.一次性 Token.全局锁.决策竞选.分布式会话和集群状态)操作的开发工 ...