您的位置:首页 > 其它

BZOJ 1189

2016-03-07 22:01 253 查看
这题是网络流最大流。

挺有趣的题,代码长度100行+,大小3000B+,Σ( ° △ °|||)︴

加强了数据后更是加大了代码难度,话说网上不少题解都不能过新数据,所以写一份(吐槽:tyvj的数据好弱,样例都WA,结果AC了。。。)。

怎么做呢?

二分是不可置否的,初始时l=0,r=sum(sum是’.’的数量),我们先二分答案mid(即时间),然后每次由门向四周遍历(BFS),将在可行时间内可以到门的’.’点记录下来,与之连流量为1的边,然后取源点S向’.’的点连流量为1的边,再由所有门向汇点连流量为mid的边,然后跑一遍最大流,若最大流等于’.’数则ans记录mid的值,然后在左边继续二分,不然就去右边二分。

好像很对,然后就

/**************************************************************
Problem: 1189
User: fantasticwtl
Language: C++
Result: 答案错误
****************************************************************/


停,这是错的,显然题目里清楚地说了:

每一秒钟只能有一个人移动到门的位置。

只是难想到反例,这有一组数据送给WA的童鞋:

Input

4 5

XXDXX

XX.XX

X…X

XXDXX

Output

3

懂了吧,这样会有两个人同时走出去的,怎么解决嘛。。。拆点大法好!

将门拆成mid个点(a1,a2,······,amid,代表时间为1秒,2秒,······,mid秒),每个点ai与汇点T连流量为1的边(完美解决了上述问题,每秒只有1人能过去),只有能在点的限制时间内到达的’.’点与ai连边,具体实现可能有所不同,主要看代码。

放一下代码,e,时间好慢(Dinic算法):

/**************************************************************
Problem: 1189
User: fantasticwtl
Language: C++
Result: 正确
Time:4600 ms
Memory:98560 kb
****************************************************************/

#include <cstdio>
#include <cmath>
#include <memory.h>
#include <algorithm>
using namespace std;
const int fx[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int qcn,sum,d[5001],g[5001][5001],q[5001],qq[1001][2],n,m,cn,k,tot[21][21],ans,kk,tt,dd[21][21],can[1001][2];
char c,enter,s[21][21];
bool sf[1001],f[21][21];
void BL(int x,int y,int p,int z)
{
memset(f,0,sizeof(f));memset(dd,0,sizeof(dd));memset(qq,0,sizeof(qq));memset(sf,0,sizeof(sf));
int H=0,T=1;
qq[1][0]=x; qq[1][1]=y; f[x][y]=1; dd[x][y]=0;
while(H<T){
H++;
int f1=qq[H][0],f2=qq[H][1];
for(int i=0;i<=3;i++){
int xx=f1+fx[i][0],yy=f2+fx[i][1];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&f[xx][yy]==0&&sf[H]==0&&s[xx][yy]=='.'){
f[xx][yy]=1;T++;qq[T][0]=xx;qq[T][1]=yy;dd[xx][yy]=dd[f1][f2]+1;
if(dd[xx][yy]==z)sf[T]=1;//时间限制
g[tot[xx][yy]][p]=1;//满足的连边
}
}
}
}
bool jz ()
{
memset(d,0,sizeof(d));
d[1]=1;q[1]=1;
int h=0,t=1;
while(h<t){
h++;
for(int i=1;i<=cn;i++)
if(d[i]==0&&g[q[h]][i]){
t++;d[i]=d[q[h]]+1;q[t]=i;
}
if(d[cn])return 1;
}
if(d[cn]==0)return 0;else return 1;
}
int find (int x,int y)
{
if(x>=cn)return y;
int a=0;
for(int i=1;i<=cn;i++)
if(d[i]==d[x]+1&&g[x][i]>0&&(a=find(i,min(y,g[x][i])))){g[x][i]-=a;g[i][x]+=a;return a;}
return 0;
}
bool pd (int x)
{
cn=qcn;
memset(g,0,sizeof(g));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(s[i][j]=='.')g[1][tot[i][j]]=1;//源点与'.'点连边
for(int i=1;i<=tt;i++)
for(int j=1;j<=x;j++){//这里就是改进
cn++;
g[cn][qcn+tt*x+1]=1;
BL(can[i][0],can[i][1],cn,j);
}
cn++;//建图部分
int q=0,w=0;
while(jz ())if(w=(find (1,100000000)))q+=w;//Dinic
return(q==sum);//是否满足
}
int main ()
{
scanf("%d%d",&n,&m);
cn=1;
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]=='.'){
cn++;
tot[i][j]=cn;
}
if(s[i][j]=='D'){
tt++;
can[tt][0]=i;
can[tt][1]=j;
}
}
}//输入部分
qcn=cn;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(s[i][j]=='.')sum++;//统计'.'的个数
int l=0,r=sum,ans=0;
while(l<=r){//二分答案
int mid=(l+r)>>1;
if(pd (mid))ans=mid,r=mid-1;//判定答案
else l=mid+1;
}
if(ans)printf("%d\n",ans);else printf("impossible\n");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: