您的位置:首页 > 其它

hdu 1733 最大流sap

2014-07-01 19:42 274 查看
给定一个矩阵,'.'表示空位,'X'表示人,'#'表示墙,'@'表示门,

每分钟每个位置至多只能站一个人,人不能穿越墙,人能从门中出去。

每个人每分钟只能上下左右移动一步,问最少需要多少时间让所有的人出去。

-----------------

//dinic TLE

----------------

(1):把每个点按照天数拆成d个点。

(2):添加源汇点scr和sink。

(3):源点向第0天地图上人所在位置的点连一天容量为1的边。

(4):枚举时间Ti

如果当前枚举的天数为d,那么地图中每个位置第d-1天的点向四周和本身第d天的点(新添加的点)连一条容量为1 的边。

(5):每次枚举只需向残留网络里重新添加一些新的点和边。然后再从源点向汇点跑最大流。

(6):直到前Ti天的最大流总和=总人数时,Ti就是所有的最少天数。

const  int  inf = 1000000000 ;
const  int  maxn = 10000 , maxm = 500000 ;
struct Edge{
int v , f ,next ;
Edge(){}
Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}
};
int  sourse , meet ;
int  id ;
Edge e[maxm*2 + 10] ;
int  g[maxn + 10] ;

void  add(int u , int v , int f){
e[++id] = Edge(v , f ,g[u]) ;
g[u] = id ;
e[++id] = Edge(u , 0 , g[v]) ;
g[v] = id ;
}
int cur[maxn] , pre[maxn] , gap[maxn] , dis[maxn] ;

void Init(){
memset(g , -1 , sizeof(g)) ;
id = 1 ;
}

int Sap(int s,int t,int n){
int ans = 0 , aug = inf;//aug表示增广路的流量
int i , v , u = pre[s] = s;
for(i = 0 ; i <= n ; i++){
cur[i] = g[i];
dis[i] = gap[i] = 0;
}
gap[s] = n;
bool flag;
while(dis[s] < n){
flag = false;
for(int &j=cur[u] ; j!=-1 ; j=e[j].next){ //&j
v = e[j].v;
if(e[j].f > 0 && dis[u] == dis[v] + 1){
flag = true;//找到容许边
aug = min(aug , e[j].f);
pre[v] = u;
u = v;
if(u == t){
ans += aug;
while(u != s){
u = pre[u];
e[cur[u]].f -= aug;
e[cur[u]^1].f += aug;//注意
}
aug = inf;
}
break;//找到一条就退出
}
}
if(flag) continue;
int mindis = n;
for(i = g[u] ; i!=-1 ; i=e[i].next){
v = e[i].v;
if(e[i].f > 0 && dis[v] < mindis){
mindis = dis[v];
cur[u] = i;
}
}
if((--gap[dis[u]]) == 0) break;
gap[dis[u] = mindis+1]++;
u = pre[u];
}
return ans;
}
//------------------------------//

int  n , m , nn ;
char str[30][30] ;
bool used[30][30] ;
int  d[4][2] = {{1,0} ,{-1,0} ,{0,1} ,{0 ,-1}} ;
int  can(int x , int y){
return 1 <= x && x <= n && 1 <= y && y <= m ;
}

int  isok(int x , int y){
used[x][y] = 1 ;
if(str[x][y] == '@') return 1 ;
for(int i = 0 ; i < 4 ; i++){
int xx = x + d[i][0] ;
int yy = y + d[i][1] ;
if(! can(xx , yy)) continue ;
if(str[xx][yy] != '#' && !used[xx][yy]){
if(isok(xx , yy)) return 1 ;
}
}
return 0 ;
}
int  summan ;
int  cannotgoout(){
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j++){
if(str[i][j] == 'X'){
summan++ ;
memset(used , 0 , sizeof(used)) ;
if(! isok(i , j)) return 1 ;
}
}
}
return 0 ;
}

int  greed(int t){
int i , j , u , k , x ,  y  , v ;
for(i = 1 ; i <= n ; i++){
for(j = 1 ; j <= m ; j++){
if(str[i][j] == '#') continue ;
u = (t-1)*n*m + (i-1)*m + j + 2 ;
add(u , u+n*m , 1) ;
for(k = 0 ; k < 4 ; k++){
x = i + d[k][0] ;
y = j + d[k][1] ;
if(! can(x , y) ) continue ;
if(str[x][y] == '#') continue ;
v = t*n*m + (x-1)*m + y + 2 ;
add(u , v , 1) ;
}
}
}
for(i = 1 ; i <= n ; i++){
for(j = 1 ; j <= m ; j++){
u = t*n*m + (i-1)*m + j + 2 ;
if(str[i][j] == '@') add(u , meet , 1) ;
}
}
nn += n*m ;
return Sap(sourse , meet , nn) ;
}

int  main(){
int i , j  , u , v , sum  , t ;
while(scanf("%d%d" , &n , &m) != EOF){
for(i = 1 ; i <= n ; i++)  scanf("%s" , str[i]+1) ;
summan = 0 ;
if(cannotgoout()){
puts("-1") ;
continue  ;
}
if(summan == 0){
puts("0")  ;
continue  ;
}
Init() ;
sourse = 1 ; meet = 2 ;
for(i = 1 ; i <= n ; i++){
for(j = 1 ; j <= m ; j++){
u = (i-1)*m + j + 2 ;
if(str[i][j] == 'X') add(sourse , u , 1) ;
}
}
nn = 2 + n*m ;
sum = 0 ;
t = 0 ;
while(sum != summan){
t++ ;
sum += greed(t) ;
}
printf("%d\n" , t) ;
}
return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: