zoukankan      html  css  js  c++  java
  • 舞会——网络流

    这是一道网络流的题目。。。

    在巨佬hy学姐&ylm学长的万般帮助下A的,万般感谢orzzzzzz


    题面有点长。。。但还是要耐心看下。。。

    做法大概就是二分答案,然后跑网络流

    • 在二分之前,先对每个人跑一边bfs,算出他从现在的格子跑到其他的格子要多久。(这里的(dis)可以存(int)类型,到后面建图(step3)判断的时候再乘上(t),这样可以减少$long $ (long)的运算复杂度但我并没有这样写):

      void bfs1(int id){
      	for(int i=1;i<=n;i++)
      		for(int j=1;j<=m;j++)
      			dis[id][i][j]=INF;
      	dis[id][a[id].x][a[id].y]=0;
      	XDDD now;
      	qd.push((XDDD){a[id].x,a[id].y,0});
      	while(!qd.empty()){
      		now=qd.front();
      		qd.pop();
      		for(int i=0;i<4;i++){
      			int xx=now.x+dir[i][0];
      			int yy=now.y+dir[i][1];
      			if(xx<1||yy<1||xx>n||yy>m||g[xx][yy]||dis[id][xx][yy]!=INF) continue;
      			dis[id][xx][yy]=now.t+a[id].t;
      			qd.push((XDDD){xx,yy,dis[id][xx][yy]});
      		}
      	}
      }
      
    
    - 二分最短时间的区间:$l=0$,$r=maxt*n*m$
    
    - 网络流的建模比较烦人,很容易出锅。。。
    
    	$ss$是源点(编号为0)
        
        $st$是汇点(编号为$2*k+2*n*m+1$)
        
        男生的点的编号从1到$k$
        
        女生的点的编号从$k+1$到$k*2$
        
        把一个格子拆成两个点,坐标为$(x,y)$的点的编号分别为$(x-1)m+y+2k$和$(x-1)m+y+2k+nm$(男生女生的点已经用掉$2k$个啦~所以这里要加上$2k$啊)
    
        $Step 1$:从源点向男生各连一条流量为1的边,从女生各向汇点连一条流量为1的边。大概长这样:
        
        ```cpp
        for(int i=1;i<=k;i++){
    		add_edge(ss,i,1);
    		add_edge(i+k,st,1);
    	}
        ```
        $Step 2$:把一个格子拆成两个点,连上一条流量为1的边:
        
        ```cpp
        for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			add_edge(turn(i,j),turn(i,j)+n*m,1);
        ```
        $Step 3$:把每个男生和他能在二分时间内到达的格子连一条流量为1边,把每个女生和她能在二分时间内到达的格子连一条流量为1边(之前把每个格子拆成两个点,这里男生女生分别连两个点):
        
        ```cpp
        for(int p=1;p<=k;p++){
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=m;j++){
    				if(dis[p][i][j]<=tim) add_edge(p,turn(i,j),1);
    				if(dis[p+k][i][j]<=tim) add_edge(turn(i,j)+n*m,p+k,1);
    			}
    		}
    	}
        ```
        嗯建完了
        
    - 然后就可以跑Dinic了,要注意时间哈
    
    完整代码:
    
    ```cpp
    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #define ll long long 
    
    using namespace std;
    const int N=40,No=1700,M=700000,Da=810,inf=0x7fffffff;
    const ll INF=0x7fffffffffffffff;
    ll l,r,maxt,ans,dis[Da][N][N];
    int n,m,k,ss,st,tot,h[No],vis[No],g[N][N],d[No];
    int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
    
    struct XDDD{int x,y;ll t;}a[Da];
    struct Edge{int to,ne,val;}e[M];
    queue<XDDD> qd;
    queue<int> q;
    
    inline int turn(int i,int j){return (i-1)*m+j+2*k;}   
    inline ll max(ll a,ll b){return a>b?a:b;}
    inline ll min(ll a,ll b){return a<b?a:b;}
    
    void add_edge(int x,int y,ll c){
    	e[++tot]=(Edge){y,h[x],c};
    	h[x]=tot;
    	e[++tot]=(Edge){x,h[y],0};
    	h[y]=tot;
    }
    
    void bfs1(int id){
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			dis[id][i][j]=INF;
    	dis[id][a[id].x][a[id].y]=0;
    	XDDD now;
    	qd.push((XDDD){a[id].x,a[id].y,0});
    	while(!qd.empty()){
    		now=qd.front();
    		qd.pop();
    		for(int i=0;i<4;i++){
    			int xx=now.x+dir[i][0];
    			int yy=now.y+dir[i][1];
    			if(xx<1||yy<1||xx>n||yy>m||g[xx][yy]||dis[id][xx][yy]!=INF) continue;
    			dis[id][xx][yy]=now.t+a[id].t;
    			qd.push((XDDD){xx,yy,dis[id][xx][yy]});
    		}
    	}
    }
    
    void build(ll tim){
    	tot=-1;
    	for(int i=0;i<=st+1;i++) h[i]=-1;
    	for(int i=1;i<=k;i++){
    		add_edge(ss,i,1);
    		add_edge(i+k,st,1);
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			add_edge(turn(i,j),turn(i,j)+n*m,1);
    	for(int p=1;p<=k;p++){
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=m;j++){
    				if(dis[p][i][j]<=tim) add_edge(p,turn(i,j),1);
    				if(dis[p+k][i][j]<=tim) add_edge(turn(i,j)+n*m,p+k,1);
    			}
    		}
    	}
    }
    
    bool bfs(){
    	while(!q.empty()) q.pop();
    	for(int i=0;i<=st+1;i++) d[i]=0;
    	d[ss]=1;
    	q.push(ss);
    	while(!q.empty()){
    		int u=q.front();
    		q.pop();
    		for(int i=h[u];i!=-1;i=e[i].ne){
    			if(!d[e[i].to]&&e[i].val){
    				d[e[i].to]=d[u]+1;
    				q.push(e[i].to);
    				if(e[i].to==st) return true;
    			}
    		}
    	}
    	return false;
    }
    
    int dfs(int u,int minn){
    	if(u==st||!minn) return minn;
    	int ret=0;
    	for(int i=h[u];i!=-1;i=e[i].ne){
    		if(d[e[i].to]==d[u]+1&&e[i].val){
    			int dd=dfs(e[i].to,min(minn,e[i].val));
    			if(dd){
    				ret+=dd;
    				minn-=dd;
    				e[i].val-=dd;
    				e[i^1].val+=dd;
    			}
    			if(!minn) break;
    		}
    	}
    	if(!ret) d[u]=-1;
    	return ret;
    }
    
    bool check(ll tim){
    	int ret=0;
    	build(tim);
    	while(bfs()) 
    		ret+=dfs(ss,inf);
    	return ret==k;
    }
    
    int main(){
    	scanf("%d%d%d
    ",&n,&m,&k);
    	char ch[25];
    	for(int i=1;i<=n;i++){
    		scanf("%s",ch);
    		for(int j=0;j<m;j++){
    			g[i][j+1]=(ch[j]=='#');
    		}
    	}
    	for(int i=1;i<=k;i++){
    		scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].t);
    		maxt=max(maxt,a[i].t);
    	}
    	for(int i=k+1;i<=k+k;i++){
    		scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].t);
    		maxt=max(maxt,a[i].t);
    	}
    	for(int i=1;i<=k+k;i++) bfs1(i);
    	ss=0;
    	st=2*k+2*n*m+1;
    	l=0;
    	r=maxt*n*m;
    	ans=-1;
    	while(l<=r){
    		ll mid=(l+r)>>1;
    		if(check(mid)){
    			ans=mid;
    			r=mid-1;
    		}else l=mid+1;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    1 Groovy
    HDU
    伸展树整理
    HYSBZ
    markdown语法整理
    HDU
    【JZOJ3085】图的计数【数论】
    【JZOJ3085】图的计数【数论】
    【JZOJ3084】超级变变变【模拟】【规律】
    【JZOJ3084】超级变变变【模拟】【规律】
  • 原文地址:https://www.cnblogs.com/Sleepy-Piggy/p/8996032.html
Copyright © 2011-2022 走看看