「网络流 24 题」[16] 星际转移

等会回来写= =
主要就是考虑用时间建点吧orz剩下等会儿回来敲。

题意

有n个空间站,地球和月球,有m个飞船在空间站和地球月球之间周期移动。对于飞船i,有一个活动表和限载人数。当一个周期结束后,飞船移动到第一个点继续。
求最少的时间把所有人疏散出去。

分析

首先其实无解什么的很好判所以不管了。
需要注意的就是对于每个飞船可以看做在某一特定时间会在空间站之间产生一条流量为限载人数的边,然后看最少在什么时候可以流完。

然后考虑枚举时间,然后加边。
然后建点的时候以(tim,x)表示时间为tim,位置在x建一个点。
枚举时间,产生边,每次在原来基础上跑一次最大流,然后瞎搞。
结束。

code
#include<bits/stdc++.h>
using namespace std;
void read(int &x){
    x=0; char c=getchar(); int p=1;
    for (;c<48;c=getchar())if (c=='-')p=-1;
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
    x*=p;
}
#define M 2005
#define inf 1000000
struct ed{
    int x,cap,nx;
}e[M*M];
int nx[M],ecnt;
void add(int x,int y,int cap){
    e[ecnt]=(ed){y,cap,nx[x]};
    nx[x]=ecnt++;
    e[ecnt]=(ed){x,0,nx[y]};
    nx[y]=ecnt++;
}

struct Dinic{
    int Flow,nnx[M],level[M],Q[M],l,r,s,t;
    bool bfs(int x){
        l=r=0;
        memset(level,0,sizeof(level));
        level[x]=1;
        Q[r++]=x;
        for (;l<r;){
            x=Q[l++];
            for (int i=nx[x];~i;i=e[i].nx)if (e[i].cap>0&&!level[e[i].x]){
                level[e[i].x]=level[x]+1;
                Q[r++]=e[i].x;
                if (e[i].x==t)return 1;
            }
        }
        return level[t]>0;
    }
    int dfs(int x,int f){
        if (x==t)return f;
        int sum=0;
        for (int &i=nnx[x];~i;i=e[i].nx)if (e[i].cap>0&&level[e[i].x]==level[x]+1){
            int d=dfs(e[i].x,min(f-sum,e[i].cap));
            e[i].cap-=d; e[i^1].cap+=d;
            sum+=d;
            if (sum==f)return f;
        }
        if (!sum)level[x]=0;
        return sum;
    }
    void solve(int ss,int tt){
        s=ss; t=tt;
        for (;bfs(s);){
            memcpy(nnx,nx,sizeof(nx));
            Flow+=dfs(s,inf);
        }
    }
}dinic;

int h[105],r[105],a[105][105];
int main(){
//  freopen("LOJ6015.in","r",stdin);
    int n,m,k,s=0,t=M-1,x,y;
    read(n); read(m); read(k);
    memset(nx,-1,sizeof(nx));
    for (int i=1;i<=m;i++){
        read(h[i]); read(r[i]);
        for (int j=0;j<r[i];j++){
            read(a[i][j]);
            if (a[i][j]==0)a[i][j]=1+n;
            if (a[i][j]==-1)a[i][j]=2+n;
        }
    }
    if (k==0){
        printf("0\n");
        return 0;
    }
    add(s,1+n,k); add(2+n,t,k);
    n+=2;
    for (int i=1;i<=n*m;i++){
        add(i*n+n,t,inf);
        for (int j=1;j<=n;j++){
            add((i-1)*n+j,i*n+j,inf);
        }
        for (int j=1;j<=m;j++){
            x=a[j][(i+r[j]-1)%r[j]]; y=a[j][i%r[j]];
            add((i-1)*n+x,i*n+y,h[j]);
        }
        dinic.solve(s,t);
        if (dinic.Flow==k){
            printf("%d\n",i);
            return 0;
        }
    }
    printf("0\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值