CF513F2,JZOJ4688躲藏

本文介绍了一种利用网络流和二分查找解决在一个二维网格中,男生和女生寻找最佳配对方案的问题,以确保每个人都能在最短的时间内找到一个配对伙伴。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

神犇ddddddpppppp勤奋好学,经常会找Fanvree大神问问题。
终于有一天,Fanvree忍无可忍(因为dp问的问题在他看来太无聊),他决定躲在某个机房让dp无法找到他。
所有的机房在一个二维平面上,可以视为一个网格图,每个网格就代表一个机房或者是杂物房。
为了不被dp发现,Fanvree找来了小伙伴帮助他。其中有A个男生,B个女生,和小标。如果每一个男生都有一个女生和他在同一个机房,且每一个女生都有一个男生和她在同一个机房,那么dp就不能找到Fanvree。由于小标CJ资料上填写的是”decline to aswer”,所以她既能和男生一对,也能和女生一对。需要注意的是:最终每一个机房最多只能有一对人,而且小标也一定要配对。每个人可以同时在四相邻的机房间移动(不能移动到杂物房),因此只要一男一女在同一个机房,他们就算是一对了。
现在Fanvree知道每一个人初始的位置和移动速度(即移动到相邻机房所需要的时间,下同)。请你帮Fanvree计算一下,在使得所有人都有人和他(她)配对的情况下,最后完成配对的时间最少是多少。

第一行,四个整数N,M,A,B,分别表示网格图大小,男生数量和女生数量。
接下来N行M列,每个位置是’.’或’#’,’.’表示机房,’#’表示杂物房。
接下来一行,三个正整数XA,YA,TA,表示小标的初始位置和移动速度。
接下来A行,每行三个正整数Xi,Yi,Ti,表示第i个男生的初始位置和移动速度。
接下来B行,每行三个正整数Xj,Yj,Tj,表示第j个女生的初始位置和移动速度。
N,M<=22, A,B<=N*M

分析

初初看到题目我是一脸懵逼。
首先小标就是唬人的,直接把他归成人少那一边。然后干什么?分配每个人到哪个房间里?明显超时。一男一女···最大匹配?题目给的是移动一格的时间,也不知道男女跑到哪个格子。
重新看题目,发现输出的是答案,那我们可以想想二分。二分最长那个人的移动距离?消耗时间?都行。
这之后我们再往之前的思路想,就发现网络流可行了。左边一排男生和S连,中间一排机房,右边一排女生和T连。注意机房拆点。然后随便跑跑~~~注意优化一下时间复杂度或者常数,因为22*22很悬。

所以说:
1,直接输出的是答案,可以往二分想;
2,根本没有分配策略的时候,想网络流吧。

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
typedef long long ll;
const ll mx=1000000007;
const int N=500000;
const int fx[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
ll te[1000],dis[1000][1000],l,r,mid;
int now[N*2],ttt,di[N*2],cnt,pdd[N*2],b[N*4],c[N*4],next[N*4],rev[N*4],first[N*2],tt,s,t,i,j,n,m,t1,t2,x[1000],y[1000],q1,q2,d[1000][3],nx,ny,pd[25][25],a[25][25],ans,mn;
void bfs()
{
    fo(i,1,t1+t2)
    {
        dis[i][x[i]*m-m+y[i]]=0;
        q1=0;q2=1;d[1][1]=x[i];d[1][2]=y[i];pd[x[i]][y[i]]=i;
        while (q1<q2)
        {
            q1++;
            fo(j,0,3)
            {
                nx=d[q1][1]+fx[j][0];
                ny=d[q1][2]+fx[j][1];
                if (a[nx][ny]=='.'&&pd[nx][ny]!=i)
                {
                    dis[i][nx*m-m+ny]=dis[i][d[q1][1]*m-m+d[q1][2]]+1;
                    d[++q2][1]=nx;
                    d[q2][2]=ny;
                    pd[nx][ny]=i;
                }
            }
        }
    }
}
void cr(int x,int y,int z,int t)
{
    tt++;
    b[tt]=y;
    c[tt]=t;
    rev[tt]=tt+z;
    next[tt]=first[x];
    first[x]=tt;
}
void make()
{
    fo(i,1,tt)
        b[i]=c[i]=rev[i]=next[i]=0;
    fo(i,s,t) first[i]=di[i]=0;
    tt=0;
    fo(i,1,t1)
    {
        cr(s,i,1,1),
        cr(i,s,-1,0);
        fo(j,1,n*m)
            if (dis[i][j]<=mid/te[i])
            {
                cr(i,j+t1+t2,1,1);
                cr(j+t1+t2,i,-1,0);
            }
    }
    fo(i,1,t2)
    {
        cr(t1+i,t,1,1);
        cr(t,t1+i,-1,0);
        fo(j,1,n*m)
            if (dis[i+t1][j]<=mid/te[i+t1])
            {
                cr(j+t1+t2+n*m,i+t1,1,1);
                cr(i+t1,j+t1+t2+n*m,-1,0);
            }
    }
    fo(i,1,n*m)
        cr(i+t1+t2,i+t1+t2+n*m,1,1),
        cr(i+t1+t2+n*m,i+t1+t2,-1,0);
}

ll maxflow(int x,int y)
{
    pdd[x]=ttt;
    if (x==t)
    {
        ans+=y;
        return y;   
    }
    for(int p=now[x];p;p=next[p])
        if (c[p]&&pdd[b[p]]!=ttt&&di[x]==di[b[p]]+1)
        {
            int l=maxflow(b[p],min(c[p],y));
            if (l)
            {
                now[x]=p;
                c[p]-=l;
                c[rev[p]]+=l;
                return l;
            }
        }
    now[x]=0;
    return 0;
}

int change()
{
    mn=mx;
    fo(i,s,t)
        if (pdd[i]==ttt)
        {
            for(int p=first[i];p;p=next[p])
                if (pdd[b[p]]!=ttt&&c[p])
                    mn=min(mn,di[b[p]]+1-di[i]);
        }
    if (mn==mx) return 0;
    fo(i,s,t)
        if (pdd[i]==ttt)
            di[i]+=mn;
    return 1;
}

int main()
{
    freopen("hide.in","r",stdin);
    scanf("%d%d%d%d\n",&n,&m,&t1,&t2);
    fo(i,1,n)
    {
        fo(j,1,m) 
        {
            scanf("%c",&a[i][j]);
            if (a[i][j]=='.') cnt++;
        }
        scanf("\n");
    }
    s=0;
    t=t1+t2+n*m*2+2;
    fo(i,1,t1+t2) fo(j,1,n*m) dis[i][j]=mx*n*m;
    scanf("%d%d%d",&l,&r,&mid);
    fo(i,1,t1) scanf("%d%d%lld",x+i,y+i,te+i);
    if (t1<t2) 
        t1++,x[t1]=l,y[t1]=r,te[t1]=mid;
    fo(i,1,t2) scanf("%d%d%d",x+i+t1,y+i+t1,te+i+t1);
    if (t1>t2) 
        t2++,x[t1+t2]=l,y[t1+t2]=r,te[t1+t2]=mid;
    if (t1!=t2)
    {
        printf("-1");
        return 0;
    }
    bfs();
    l=0;
    r=cnt*mx;
    while (l<r)
    {
        mid=(l+r)/2;
        make();
        ans=0;
        do
        {
            fo(i,s,t) now[i]=first[i];
            ttt++;
            while (maxflow(0,mx)) 
                ttt++;
        }while(change());
        if (ans==t1) r=mid;
        else l=mid+1;
    }
    if (r==cnt*mx) r=-1;
    printf("%lld",r);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值