[国家集训队]航班安排

航班安排

题解

很简单的一道网络流

看到这道题应该是十分容易想起费用流。
很明显,如果我们将每个单位的时间的机场分别建点再出来连的话是明显会T的,因为这样边的数量达到了 n ( T + m ) n(T+m) n(T+m)的级别。
考虑不通过机场来建点,通过询问来建点。
将每个询问拆成入点与出点,两者之间连流量为 1 1 1,费用为 − c -c c的边,再在所有的询问之间判断哪些出点能够到达哪些入点,在它们间连边。最后再连出起点与终点的边即可。
至于总共 K K K台飞机的限制,只要再加一个超源点,将它与源点之间边的流量设为 K K K即可。

这种方法建出来点数是 2 m + 4 2m+4 2m+4,边数是 m 2 m^2 m2级别的。
时间复杂度 O ( K m 3 ) O\left(Km^3\right) O(Km3),当然,如果它卡了spfa的话。但事实证明没有

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 205
#define MAXT 3005
#define MAXM 505
#define MAXX 250005
#define reg register
typedef long long LL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
const LL inf=0x7f7f7f7f7f7f;
template<typename _T>
inline void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,K,Ti,head[MAXM],tot,pre[MAXM],pw[MAXM],dis[MAXM],cnt,S1,T1;
int tim[MAXN][MAXN],pai[MAXN][MAXN],S,T,fl[MAXM];bool link[MAXN][MAXT];
queue<int> q;bool vis[MAXM];
struct edge{int to,nxt,flow,paid,op;}e[MAXX];
struct ming{int a,b,s,t;}s[MAXN];
inline void addEdge(const int u,const int v,const int w,const int f){e[++tot]=(edge){v,head[u],f,w};head[u]=tot;}
inline void addedge(const int u,const int v,const int f,const int w){addEdge(u,v,w,f);e[tot].op=tot+1;addEdge(v,u,-w,0);e[tot].op=tot-1;}
inline pii EK(){
	int mf=0,res=0;
	while(1){
		while(!q.empty())q.pop();
		for(reg int i=1;i<=cnt;++i)dis[i]=INF,fl[i]=0;
		q.push(S);dis[S]=0;vis[S]=1;fl[S]=INF;
		while(!q.empty()){
			const int u=q.front();q.pop();vis[u]=0;if(!fl[u])continue;
			for(reg int i=head[u];i;i=e[i].nxt){
				const int v=e[i].to;
				if(dis[v]>dis[u]+e[i].paid&&e[i].flow>0){
					dis[v]=dis[u]+e[i].paid;pre[v]=u;pw[v]=i;
					if(!vis[v])q.push(v),vis[v]=1,fl[v]=min(fl[u],e[i].flow);
				}
			}
		}
		if(dis[T]>INF-1)break;mf+=fl[T];res+=fl[T]*dis[T];
		for(reg int i=T;i^S;i=pre[i])e[pw[i]].flow-=fl[T],e[e[pw[i]].op].flow+=fl[T];
	}
	return make_pair(mf,res);
}
signed main(){
	read(n);read(m);read(K);read(Ti);S=m*2+1;T=m*2+2;S1=m*2+3;cnt=T1=m*2+4;
	for(reg int i=0;i<n;++i)for(reg int j=0;j<n;++j)read(tim[i][j]);
	for(reg int i=0;i<n;++i)for(reg int j=0;j<n;++j)read(pai[i][j]);
	for(reg int i=1;i<=m;++i){
		int c;read(s[i].a),read(s[i].b),read(s[i].s);read(s[i].t);read(c);
		if(tim[0][s[i].a]>s[i].s||s[i].t+tim[s[i].b][0]>Ti)continue;
		addedge(S1,2*i-1,1,pai[0][s[i].a]);addedge(2*i,T1,1,pai[s[i].b][0]);addedge(2*i-1,2*i,1,-c);
		for(reg int j=1;j<i;++j){
			if(s[i].s-tim[s[j].b][s[i].a]>=s[j].t)addedge(2*j,2*i-1,1,pai[s[j].b][s[i].a]);
			if(s[i].t+tim[s[i].b][s[j].a]<=s[j].s)addedge(2*i,2*j-1,1,pai[s[i].b][s[j].a]);
		} 
	}
	addedge(S,S1,K,0);addedge(T1,T,K,0);
	pii ans=EK();printf("%d\n",-ans.second);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值