1. 程式人生 > >【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)A】【floyd 最小路徑覆蓋】最少飛機數滿足所有航班要求

【2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div 1)A】【floyd 最小路徑覆蓋】最少飛機數滿足所有航班要求

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=500+5,M=0,Z=1e9+7,ms63=1061109567;
int n,m;
int p[N];
int t[N][N],f[N][N];
pair<int,int>a[N],b[N];
int match[N];
bool vis[N];
vector<int>w[N];
void floyd()
{
	MC(f,t);
	for(int k=1;k<=n;++k)
	{
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				gmin(f[i][j],f[i][k]+f[k][j]);
			}
		}
	}
}
int find(int x)
{
	vis[x]=1;
	for(int i=w[x].size()-1;~i;--i)
	{
		int y=w[x][i];
		if(match[y]==-1)
		{
			match[y]=x;
			return 1;
		}
	}
	for(int i=w[x].size()-1;~i;--i)
	{
		int y=w[x][i];
		if(!vis[match[y]]&&find(match[y]))
		{
			match[y]=x;
			return 1;
		}
	}
	return 0;
}
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		for(int i=1;i<=n;++i)scanf("%d",&p[i]);
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				scanf("%d",&t[i][j]);
				if(i!=j)t[i][j]+=p[j];
			}
		}
		floyd();
		for(int i=1;i<=m;++i)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			a[i]=MP(x,z);
			b[i]=MP(y,z+t[x][y]);
		}
		for(int i=1;i<=m;++i)
		{
			w[i].clear();
			for(int j=1;j<=m;++j)if(i!=j&&b[i].second+f[b[i].first][a[j].first]<=a[j].second)w[i].push_back(j);
		}
		int ans=0;
		MS(match,-1);
		for(int i=1;i<=m;++i)
		{
			MS(vis,0);
			if(find(i))++ans;
		}
		printf("%d\n",m-ans);
	}
	return 0;
}
/*
【trick&&吐槽】
1,連邊的基準是狀態,而不是按照機場。
2,匹配的時候一定要防止自己連自己。
3,不同機場之間,能否達到是要用floyd之後的時間。

【題意】
有n(1<=n<=500)個機場。
每兩個機場之間有一個抵達時間,i->j的時間被我們記錄在a[i][j]中。
同時,如果飛機還要再飛,那到達的機場i還要對飛機做檢修,花費時間為p[i];

記下來還有m(1<=m<=500)條航線,
對於第i條航線,有si,fi,ti,表示我們從需要有一架飛機,恰好於時刻ti出發,si直飛到fi。
問你這個公司至少要提供多少架飛機。

【型別】
floyd 最小路徑覆蓋

【分析】
這道題,我們有m條航線。{si,fi,ti}表示於時刻ti出發,從si到fi的一條航線。可以被標記為一個點。
顯然,我們最多隻會需要m架飛機。

每個點被自然分配成了2個點,起點和終點,都附帶一個事件屬性。
如果A的終點和B的起點,可以在時間內相通,那麼我們就可以減少一架飛機。
於是,只要floyd後最小路徑覆蓋就可以解決這道題。

問題來了——
1,floyd可以求出從x到y的時間,因為我們還要保留直達時間,所以,floyd矩陣需要額外算。
	我們要把所有抵達點的維修時間算上,這樣它才可以繼續起飛。
2,最小路徑覆蓋,我們需要確定什麼樣的兩個點之間可以連邊。
	什麼樣的兩個點之間可以連邊呢?點A的終點時間+(A到B的時間)<=點B的起點時間的時候。

這道題就這樣做完啦。

【時間複雜度&&優化】
O(n^3)

*/