舞会——网络流

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

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

1391536-20180505204627469-747747819.png
1391536-20180505204719590-121910544.png

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

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

  • 在二分之前,先对每个人跑一边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的边。大概长这样:

    for(int i=1;i<=k;i++){
        add_edge(ss,i,1);
        add_edge(i+k,st,1);
    }

    \(Step 2\):把一个格子拆成两个点,连上一条流量为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);

    \(Step 3\):把每个男生和他能在二分时间内到达的格子连一条流量为1边,把每个女生和她能在二分时间内到达的格子连一条流量为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);
            }
        }
    }

    嗯建完了

  • 然后就可以跑Dinic了,要注意时间哈

完整代码:

#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",&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;
}

转载于:https://www.cnblogs.com/Sleepy-Piggy/p/8996032.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值