BZOJ 2696 航班安排 费用流

题目大意:K架飞机,N个机场,以0..N-1编号,其中0号为基地机场,每天0时刻起飞机从该机场起飞,并不晚于T时刻回到该机场。M个包机请求,每个请求为在s时刻从a机场起飞,在恰好t时刻到达b机场,可以净获利c。机场之间来往有花费。求最大收益。

将每一个请求拆成两个点,在两个点之间连一条费用为负的完成请求x的利润,流量为1的边,表示完成请求获得了利润且只能完成一次。在0时刻可以选择完成任意一个请求x,若能不晚于请求x开始的时间到达请求x的起始点,则连一条从S到x,费用为从基地到x的起始点的花费,容量为INF的边;在完成一个请求后可以选择完成另一个请求或是直接返回基地,若能不晚于规定时间T回到基地,则连一条从x到T,费用为负的请求x的结束点到基地的费用,容量为INF的边;再判断请求之间的关系,若完成一个请求x后能从请求x的结束点另一个请求y开始的时间之前到达请求y的起始点,就连一条从x到y,费用为从x的结束点到y的开始点的花费,容量为INF的边。

跑一边最小费用可行流后将费用取相反数即最大利润。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 505
#define INF 1000000000
using namespace std;
struct Edge {
    int from,to,nxt,cap,cost;
}e[N*N];
struct Quest {
    int a,b,s,t,c;
    void scan() {scanf("%d%d%d%d%d",&a,&b,&s,&t,&c);}
}a[N];
int n,m,k,S,T,tim_lim,top=-1,t[N][N],c[N][N],fir[N];
void Add_Edge(int from,int to,int cap,int cost) {
    e[++top].from=from;
    e[top].to=to;
    e[top].cap=cap;
    e[top].cost=cost;
    e[top].nxt=fir[from];
    fir[from]=top;
    e[++top].from=to;
    e[top].to=from;
    e[top].cap=0;
    e[top].cost=-cost;
    e[top].nxt=fir[to];
    fir[to]=top;
    return ;
}
bool SPFA(int& flow,int& cost) {
    static bool inq[N];
    static int f[N],d[N],p[N];
    for(int i=S;i<=T;i++) d[i]=INF;
    memset(inq,false,sizeof inq);
    d[S]=0;
    inq[S]=true;
    p[S]=0;
    f[S]=INF;
    queue<int> q;
    q.push(S);
    while(!q.empty()) {
        int x=q.front(); q.pop();
        inq[x]=false;
        for(int i=fir[x];~i;i=e[i].nxt) {
            if(e[i].cap<1 || d[e[i].to]<=d[x]+e[i].cost) continue;
            d[e[i].to]=d[x]+e[i].cost;
            p[e[i].to]=i;
            f[e[i].to]=min(f[x],e[i].cap);
            if(!inq[e[i].to]) q.push(e[i].to), inq[e[i].to]=true;
        }
    }
    if(d[T]>=0) return false;
    flow+=f[T];
    cost+=d[T]*f[T];
    int x=T;
    while(x!=S) {
        e[p[x]].cap-=f[T];
        e[p[x]^1].cap+=f[T];
        x=e[p[x]].from;
    }
    return true;
}
int CostFlow() {
    int flow=0,cost=0;
    while(SPFA(flow,cost));
    return cost;
}
int main() {
    memset(fir,-1,sizeof fir);
    scanf("%d%d%d%d",&n,&m,&k,&tim_lim);
    S=0, T=m*2+2;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            scanf("%d",&t[i][j]);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            scanf("%d",&c[i][j]);
    for(int i=1;i<=m;i++) a[i].scan();
    for(int i=1;i<=m;i++) {
        Add_Edge(i*2,i*2+1,1,-a[i].c);
        for(int j=1;j<=m;j++)
            if(a[i].t+t[a[i].b][a[j].a]<=a[j].s)
                Add_Edge(i*2+1,j*2,INF,c[a[i].b][a[j].a]);
        if(t[0][a[i].a]<=a[i].s) Add_Edge(S+1,i*2,INF,c[0][a[i].a]);
        if(a[i].t+t[a[i].b][0]<=tim_lim) Add_Edge(i*2+1,T,INF,c[a[i].b][0]);
    }
    Add_Edge(S,S+1,k,0);
    printf("%d\n",-CostFlow());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值