Description

作为一个旅行达人以及航空公司的金卡会员,你每一年的飞行里程可以绕赤道几周了。你发现,航空公司为了提高飞机的使用率,并不是简单的一条航线使用一架飞机来回飞,而是会让同一架飞机连续不停地飞不同的航线,甚至有的时候为了能够完成飞机的调度,航空公司还会增开一些临时航线——在飞机转场的同时顺路捎一些乘客。你研究了一下GDOI著名航空公司GD Airways的常规直飞航线,你想知道,在最佳调度方案下,GD Airways最少需要多少架飞机才能成功执飞这所有的航线。
GDOI王国里有N个机场,编号为1到N。从i号机场到j号机场需要飞行Ti,j的时间。由于风向,地理位置和航空管制的因素,Ti,j和Tj,i并不一定相同。
此外,由于飞机降落之后需要例行维修和加油。当一架飞机降落k号机场时,需要花费Pk的维护时间才能再次起飞。
GD Airways一共运营M条航线,其中第i条直飞航线需要在Di时刻从Xi机场起飞,不经停,飞往Yi机场。
为了简化问题,我们假设GD Airways可以在0时刻在任意机场任意多架加油维护完毕的飞机;为了减少飞机的使用数,我们允许GD Airways增开任意多条临时航线以满足飞机的调度需要。
你想知道,理论上GD Airways最少需要多少架飞机才能完成所有这M个航班。

Solution

一眼就可以看出来是最小路径覆盖
根据常识最小路径覆盖=节点个数-最大匹配
如果航班i的飞机可以及时飞到航班j,那么就可以把i向j连边。
然后可以用KM算法(匈牙利算法),也可以打一个最大流。
其实还有一个贪心的算法(不知道是不是水的),这里就不谈了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1007;
int i,j,k,l,t,n,m,ans,S,T;
int first[maxn*maxn],next[maxn*maxn],last[maxn*maxn],chang[maxn*maxn];
int p[maxn*maxn],a[maxn][maxn],f[maxn][maxn],d[maxn],fan[maxn*maxn],num;
int data[maxn*maxn],b[maxn*maxn],c[maxn*maxn],dd[maxn*maxn],dis[maxn*maxn];
void add(int x,int y,int z){
last[++num]=y;
next[num]=first[x];
first[x]=num;
chang[num]=z;
fan[num]=num+1;
last[++num]=x;
next[num]=first[y];
first[y]=num;
chang[num]=0;
fan[num]=num-1;
}
bool bfs(){
int head=0,tail=1,now,i,ber;
memset(data,0,sizeof(data));
memset(d,0,sizeof(d));d[S]=1;
ber=d[0];
data[1]=S;
while(head<tail){
now=data[++head];
for(i=first[now];i;i=next[i]){
if(!d[last[i]]&&chang[i]>0){
d[last[i]]=d[now]+1;
data[++tail]=last[i];
}
}
}
return d[T]!=0;
}
int dinic(int x,int y){
int i,j,k=0,l=0;
if(x==T){
return y;
}
for(i=first[x];i;i=next[i]){
if(d[x]+1==d[last[i]]&&chang[i]>0){
if(chang[i]>y)k=dinic(last[i],y);
else k=dinic(last[i],chang[i]);
if(k){
l+=k;
chang[i]-=k;chang[fan[i]]+=k;
y-=k;
if(y==0)break;
}
}
}
if(l==0)d[x]=-1;
return l;
}
int main(){
freopen("flight.in","r",stdin);
freopen("flight.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n)scanf("%d",&p[i]);
S=0,T=2*m+1;
memset(f,127,sizeof(f));
fo(i,1,n)fo(j,1,n){
scanf("%d",&a[i][j]);
if(i==j)f[i][j]=a[i][j];
else f[i][j]=a[i][j]+p[j];
}
fo(k,1,n){
fo(i,1,n){
fo(j,1,n){
if(i==j)continue;
if(f[i][k]+f[k][j]<f[i][j]){
f[i][j]=f[i][k]+f[k][j];
}
}
}
}
fo(i,1,m){
scanf("%d%d%d",&b[i],&c[i],&dd[i]);
add(S,i,1);add(i+m,T,1);
}
fo(i,1,m){
fo(j,1,m){
if(i==j)continue;
if(dd[i]+a[b[i]][c[i]]+p[c[i]]+f[c[i]][b[j]]<=dd[j]){
add(i,j+m,1);
}
}
}
ans=m;
while(bfs())ans-=dinic(S,0x7fffffff);
printf("%d\n",ans);
}