题目传送门
这道题真的是一道好题啊!!!!
表示做了两个小时。。
bzoj的数据是真的强(pi)。
一开始yy了个图结果发现错了。
上网看了看题解。按照构图敲了个代码。
错了!!!
听说被一组神数据卡掉了。
数据如下。
4 5
XXDXX
XX.XX
X…X
XXDXX
按照题解的方法跑出来答案是2但是手算是3。
很无语,只好自己yy了。。
所以想到了拆点。
建图是这样的:
st连接每个空地,流量为1(表示每个空地一开始有一个人)
每个人去找每一个门。
假设当前这个人距离某一个门为t。
如果t<=当前规定时间的话。很明显可以到达。所以连边。
那么他走过去就需要t的时间。
但是呢,因为同一时间不能有两个人通过同一个门。所以他有可能就是t+1的时间到的。
也有可能是t+2时间到的,还有其他的可能。
所以说我们把每一个门都拆成若干个点。
第一个点代表这个门在第1个时间点里到的人。
第二个点表示这个门在第2个时间点里到的人。
以此类推。
然后每一个门的点都去连接ed,容量为1。
表示的是这个门在这个时间点里只能有一个人通过(因为我已经拆点了嘛)
建好图跑最大流,最大流量等于在这个时间限制下最多能通过多少人。
如果全部都可以通过那么继续往小的二分。
代码实现:
#include
#include
#include
#include
#include
using namespace std;
struct node {
int x,y,c,next,other;
}a[2100000];int len,last[6100000];
void ins(int x,int y,int c) {
int k1,k2;
len++;k1=len;
a[len].x=x;a[len].y=y;a[len].c=c;
a[len].next=last[x];last[x]=len;
len++;k2=len;
a[len].x=y;a[len].y=x;a[len].c=0;
a[len].next=last[y];last[y]=len;
a[k1].other=k2;
a[k2].other=k1;
}
int st,ed,head,tail,list[210000],h[210000];
bool bfs() {
memset(h,0,sizeof(h));h[st]=1;
head=1;tail=2;list[1]=st;
while(head!=tail) {
int x=list[head];
for(int k=last[x];k;k=a[k].next) {
int y=a[k].y;
if(h[y]==0&&a[k].c>0) {
h[y]=h[x]+1;
list[tail++]=y;
if(tail==ed+1)
tail=1;
}
}
head++;
if(head==ed+1)
head=1;
}
if(h[ed]==0)
return false;
return true;
}
int findflow(int x,int f) {
if(x==ed)
return f;
int s=0,t;
for(int k=last[x];k;k=a[k].next) {
int y=a[k].y;
if(a[k].c>0&&h[y]==h[x]+1&&sif(s==0)
h[x]=0;
return s;
}
int d[31][31][31][31]; //d[i][j][x][y]表示(i,j)到(x,y)的距离
struct dian {
int x,y;
}llist[410];int n,m;
bool v[31][31],map[31][31];
int dx[5]={-1,0,1,0};
int dy[5]={0,1,0,-1};
bool pd(int x,int y) {
if(x<1||y<1||x>n||y>m)
return false;
return true;
}
char ss[31][31];
void bfs(int stx,int sty) { //求出(stx,sty)这个位置到其他点的距离
head=1;tail=2;llist[1].x=stx;llist[1].y=sty;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
d[stx][sty][i][j]=999999999;
d[stx][sty][stx][sty]=0;
memset(v,false,sizeof(v));v[stx][sty]=true;
while(head!=tail) {
dian tno=llist[head];
int x=tno.x,y=tno.y;
for(int i=0;i<=3;i++) {
int tx=x+dx[i],ty=y+dy[i];
if(d[stx][sty][tx][ty]>d[stx][sty][x][y]+1&&map[tx][ty]==true&&pd(tx,ty)==true) { //满足条件才可以走。
d[stx][sty][tx][ty]=d[stx][sty][x][y]+1;
if(v[tx][ty]==false&&ss[tx][ty]!='D') { //遇到一个门就要进去,不能再去别的地方了,所以门是不可以进入队列的。
v[tx][ty]=true;
llist[tail].x=tx;llist[tail++].y=ty;
}
}
}
head++;
}
}
bool f[410][410];
struct Dian {
int x,y;
}t[1100];
int main() {
scanf("%d%d",&n,&m);
memset(map,true,sizeof(v));
int s=0,A=0;
for(int i=1;i<=n;i++) {
scanf("%s",ss[i]+1);
for(int j=1;j<=m;j++) {
if(ss[i][j]=='X')
map[i][j]=false;
if(ss[i][j]=='D') //t数组存D的信息
t[++s].x=i,t[s].y=j;
if(ss[i][j]=='.')
A++; //A表示有多少块空地
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(ss[i][j]=='.')
bfs(i,j); //求每一块空地到别的地方的距离
int l=1,r=400,mid,ans=-1;
//点的编号我是这样排的。空地为1~n*m,门为n*m+1~n*m+mid*s
//因为s表示有多少个门,每个门又拆成了若干个点(mid)
while(l<=r) {
mid=(l+r)/2; //mid就是二分的时间限制
len=0;memset(last,0,sizeof(last));
st=n*m+mid*s+1,ed=st+1; //每次的st和ed
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(ss[i][j]=='.')
ins(st,(i-1)*m+j,1); //st到每块空地容量为1
int sss=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(ss[i][j]=='.')
for(int k=1;k<=s;k++) {
Dian D=t[k];
int dd=d[i][j][D.x][D.y]; //距离等于时间。
while(dd<=mid) {
ins((i-1)*m+j,n*m+s*(dd-1)+k,1); //每一种可能都要连边
dd++;
}
}
for(int i=n*m+1;i<=n*m+mid*s;i++) //每个门都去连ed
ins(i,ed,1);
int sum=0;
while(bfs()==true)
sum+=findflow(st,999999999);
if(sum==A) { //如果最后通过的人数等于空地数那也就是合法了。
r=mid-1;ans=mid;
}
else
l=mid+1;
}
if(ans==-1)
printf("impossible\n");
else
printf("%d\n",ans);
return 0;
}
这道题真是好题,考验构图能力。
力荐!!!