题意:

有n个地点,有g个人要去医院疗伤,他们最开始在in,在T时间内要到m个医院的某一个,有m条x到y的有向边,每条边每个单位时间可以通过pi人,走到这里需要ti个单位时间。

人可以停留在任意一个点,求最多能有多少人到达医院。


思路:

可以看出是一道最大流。

那么如何建图呢?

按时间拆点,比如n=4,t=5拆成:

D0C21661627A509C720A76B99CB28BCA

超级源点向初始地点连边,容量为g,表示0时刻初始人数为g。

每个点的时刻向下个时刻连边,容量为INF/g,表示可以原地不动。

每个医院向超级汇点连边,容量为INF,表示一到医院就无限停留到汇点。

对于x到y的一条边,每个t的x向t+ti的y连边,容量为pi,表示在t时刻有pi个人可以从x到y,到达y的时间为t+ti。

其他就是要注意数组的大小。

每次做完网络流就觉得妙啊。


代码:

int s,e;
int top;
struct edge
{
    int to;
    int cap;
    int next;
}eg[10000100];
int head[200100];
int cur[200100];
int dis[200100];
void add(int x,int y,int z)
{
    eg[top].cap=z;
    eg[top].to=y;
    eg[top].next=head[x];
    head[x]=top++;
}
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    queue<int>q;
    dis[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int id=q.front();
        q.pop();
        for(int i=head[id];i!=-1;i=eg[i].next)
        {
            int temp=eg[i].to;
            if(dis[temp]==-1&&eg[i].cap>0)
            {
                dis[temp]=dis[id]+1;
                q.push(temp);
                if(temp==e)break;
            }
        }
    }
    return dis[e]!=-1;
}
int dfs(int id,int cp)
{
    if(id==e||cp==0)return cp;
    int res=0,f;
    for(int i=cur[id];i!=-1;i=eg[i].next)
    {
        int temp=eg[i].to;
        if(dis[temp]==dis[id]+1)
        {
            f=dfs(temp,min(cp-res,eg[i].cap));
            if(f>0)
            {
                eg[i].cap-=f;
                eg[i^1].cap+=f;
                res+=f;
                if(res==cp)return cp;
            }
        }
    }
    if(res==0)dis[id]=-1;
    return res;
}
int dinic()
{
    int ans=0;
    while(bfs())
    {
        for(int i=s;i<=e;i++)
            cur[i]=head[i];
        ans+=dfs(s,INF);
    }
    return ans;
}
int main()
{
    //freopen("/Users/apple/Downloads/problemset/testdata/A.in","r",stdin);
    int t,n,in,g,k,m,x,r,a,b,p,tt;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        scanf("%d%d%d",&in,&g,&k);
        top=0;
        memset(head,-1,sizeof(head));
        s=0;e=(k+1)*n+1;
        add(s,in,g);
        add(in,s,0);
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d",&x);
            for(int i=0;i<=k;i++)
            {
                add(x+i*n,e,g);
                add(e,x+i*n,0);
            }
        }
        for(int i=1;i<=n;i++)
            for(int j=0;j<k;j++)
            {
                add(i+j*n,i+(j+1)*n,g);
                add(i+(j+1)*n,i+j*n,0);
            }
        scanf("%d",&r);
        while(r--)
        {
            scanf("%d%d%d%d",&a,&b,&p,&tt);
            for(int i=0;i+tt<=k;i++)
            {
                add(a+i*n,b+(i+tt)*n,p);
                add(b+(i+tt)*n,a+i*n,0);
            }
        }
        printf("%d\n",min(dinic(),g));
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

旅行商问题 Previous
BAPC2014FinalK 折半搜索(中途相遇法) Next