洛谷 P4452 [国家集训队]航班安排 最小费用最大流 Dinic+Spfa 最大收益(边dis值存负)转化为最小费用

题目链接:

https://www.luogu.com.cn/problem/P4452

思路来源博客:

https://www.luogu.com.cn/blog/lxy-tql/solution-p4452

算法:1:最小费用最大流 Dinic+Spfa 最大收益(边dis值存负)转化为最小费用

思路:

1:以请求为点进行建图,对每个请求进行拆点,拆点后两个点之间连价值为 c ,流量为 1 的边,代表着一个请求只能执行一次

2:然后我们考虑时间限制:对于一个请求,如果 0时刻可以从 0机场飞到该请求的起点机场,那么源点向该请求连价值为( − 飞行费用),流量为inf的边,同理,若一个请求的结束时间,加上它的结束机场飞回 0 的时间小于等于总的时间限制,该请求向汇点连边

3:但是每次执行完一个请求并未规定一定要飞回 0 机场,也可以飞去其他请求的起点机场,所以两两枚举请求,如果满足时间条件也进行连边

4:最后考虑有 k架飞机,所以再建一个源点,向原来的源点连费用为 0 ,流量为 k的边即可,最后跑最大费用最大流

样例图解:

代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn=4e2+3,maxm=8e4+8e2+3,inf=0x7fffffff;
int k,tlim,tij[200][200],fij[200][200];
int n,m,s,t,tot=1,head[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn],maxflow,mincost;
//dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量
bool vis[maxn];
queue<int>q;

struct edge
{
    int to,next,w,dis;//w流量 dis花费
}e[maxm];

struct renwu
{
    int a,b,s1,t1,c;
}r[201];

void addedge(int x,int y,int w,int dis)//注意这道题目求最大收益,那么边的dis值存负的,
{                                                              //然后求出的最小费用取负值就是要求的最大收益
    e[++tot].to=y;e[tot].w=w;e[tot].dis=-dis;e[tot].next=head[x];head[x]=tot;
    e[++tot].to=x;e[tot].w=0;e[tot].dis=dis;e[tot].next=head[y];head[y]=tot;
}

bool spfa(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(flow,0x7f,sizeof(flow));
    memset(vis,0,sizeof(vis));
    q.push(s);vis[s]=1;dis[s]=0;pre[t]=-1;
    while(!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for(int i=head[now];i;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].w>0&&dis[y]>dis[now]+e[i].dis)//相当于求最短路
            {
                dis[y]=dis[now]+e[i].dis;
                pre[y]=now;
                last[y]=i;
                flow[y]=min(flow[now],e[i].w);
                if(!vis[y])
                {
                    vis[y]=1;q.push(y);
                }
            }
        }
    }
    return pre[t]!=-1;
}

void dfs()
{
    int now=t;
    maxflow+=flow[t];
    mincost+=flow[t]*dis[t];
    while(now!=s)//从汇点一直回溯到源点
    {
        e[last[now]].w-=flow[t];
        e[last[now]^1].w+=flow[t];
        now=pre[now];
    }
}

int main()
{
    ios::sync_with_stdio(0);
    scanf("%d %d %d %d",&n,&m,&k,&tlim);
    s=0;t=2*m+1;
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)scanf("%d",&tij[i][j]);
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)scanf("%d",&fij[i][j]);
    for(int i=1;i<=m;i++)scanf("%d %d %d %d %d",&r[i].a,&r[i].b,&r[i].s1,&r[i].t1,&r[i].c);
    for(int i=1;i<=m;i++)
    {
        addedge(i,i+m,1,r[i].c);
        if(r[i].t1+tij[r[i].b][s]<=tlim)addedge(i+m,t,inf,-fij[r[i].b][s]);
        else continue;
        if(tij[s][r[i].a]<=r[i].s1)addedge(s,i,inf,-fij[s][r[i].a]);
        for(int j=1;j<=m;j++)
        {
            if(r[i].t1+tij[r[i].b][r[j].a]<=r[j].s1)addedge(i+m,j,inf,-fij[r[i].b][r[j].a]);
        }
    }
    s=t+1;
    addedge(s,0,k,0);
    while(spfa(s,t))dfs();
    printf("%d\n",-mincost);
    return 0;
}
发布了164 篇原创文章 · 获赞 82 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/aiwo1376301646/article/details/104382129
今日推荐