Destroying the bus stations

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3688 Accepted Submission(s): 1253

Problem Description
Gabiluso is one of the greatest spies in his country. Now he’s trying to complete an “impossible” mission ----- to make it slow for the army of City Colugu to reach the airport. City Colugu has n bus stations and m roads. Each road connects two bus stations directly, and all roads are one way streets. In order to keep the air clean, the government bans all military vehicles. So the army must take buses to go to the airport. There may be more than one road between two bus stations. If a bus station is destroyed, all roads connecting that station will become no use. What’s Gabiluso needs to do is destroying some bus stations to make the army can’t get to the airport in k minutes. It takes exactly one minute for a bus to pass any road. All bus stations are numbered from 1 to n. The No.1 bus station is in the barrack and the No. n station is in the airport. The army always set out from the No. 1 station.
No.1 station and No. n station can’t be destroyed because of the heavy guard. Of course there is no road from No.1 station to No. n station.

Please help Gabiluso to calculate the minimum number of bus stations he must destroy to complete his mission.

Input
There are several test cases. Input ends with three zeros.

For each test case:

The first line contains 3 integers, n, m and k. (0< n <=50, 0< m<=4000, 0 < k < 1000)
Then m lines follows. Each line contains 2 integers, s and f, indicating that there is a road from station No. s to station No. f.

Output
For each test case, output the minimum number of stations Gabiluso must destroy.

Sample Input
5 7 3
1 3
3 4
4 5
1 2
2 5
1 4
4 5
0 0 0

Sample Output
2

Source
2008 Asia Regional Beijing


网络流+最短路

如果题目是让我们割掉一些点,使得起点无法到达终点的最少割点数,那么直接拆点跑最小割即可。

现在变成了,最短距离大于K的最少割点数。

我们可以从建图考虑,如果我们建图时,加入的边都是1到n的最短路小于等于K的边,那么是不是我们无论怎么走到n,最短路都是小于等于K的,所以我们从必须要割的点入手,减少那些肯定不会对答案进行贡献的边即可。

怎么判断加入的边呢?d[i][j] 表示 i 到 j 的最短路,那么则:

d[1][a] + d[a][b] + d[b][n] <= k ,那么边(a,b)就是我们需要的边,然后这里我们d[a][b] = 1。

注意这种做法是错误的,因为我们割点之后,最短路就被改变了!!!所以还是用下面的费用流吧

hack数据:

8 10 5
1 2
2 3
3 4
4 5
5 6
6 8
1 7
7 8
4 7
7 4

ans : 1

感谢大佬 宋聚聚 的hack数据。


WA代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e4+10,M=1e5+10;
int n,m,k,s,t,h[N],a[N],b[N],g[N][N];
int head[N],to[M],w[M],nex[M],tot;
inline void ade(int a,int b,int c){
	to[++tot]=b; w[tot]=c; nex[tot]=head[a]; head[a]=tot;
}
inline void add(int a,int b,int c){
	ade(a,b,c);	ade(b,a,0);
}
inline void init(){
	tot=1; memset(head,0,sizeof head); t=n; s=1+n;
	for(int i=1;i<=n;i++)	for(int j=1;j<=n;j++)	g[i][j]=(i==j)?0:inf;
}
void floyd(){
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)	g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
inline int bfs(){
	queue<int> q;	q.push(s); memset(h,0,sizeof h); h[s]=1;
	while(q.size()){
		int u=q.front();	q.pop();
		for(int i=head[u];i;i=nex[i]){
			if(w[i]&&!h[to[i]]){
				h[to[i]]=h[u]+1;	q.push(to[i]);
			}
		}
	}
	return h[t];
}
int dfs(int x,int f){
	if(x==t)	return f; int fl=0;
	for(int i=head[x];i&&f;i=nex[i]){
		if(w[i]&&h[to[i]]==h[x]+1){
			int mi=dfs(to[i],min(w[i],f));
			w[i]-=mi; w[i^1]+=mi; fl+=mi; f-=mi;
		}
	}
	if(!fl) h[x]=-1;
	return fl;
}
int dinic(){
	int res=0;
	while(bfs())	res+=dfs(s,inf);
	return res;
}
signed main(){
	while(~scanf("%d %d %d",&n,&m,&k),n+m+k){
		init(); 
		for(int i=1;i<=m;i++){
			scanf("%d %d",&a[i],&b[i]);	g[a[i]][b[i]]=1;
		}
		floyd();
		for(int i=1;i<=n;i++)	add(i,i+n,1);
		for(int i=1;i<=m;i++){
			if(g[1][a[i]]+1+g[b[i]][n]<=k)	add(a[i]+n,b[i],inf);
		}
		printf("%d\n",dinic());
	}
	return 0;
}

费用流解法:

我们考虑把费用(路径长度)小于等于K的加入答案即可,就能保证其他的路径一定费用是大于K的,满足条件。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e3+10,M=1e5+10;
int n,m,k,s,t,d[N],v[N],e[N];
int head[N],nex[M],to[M],w[M],flow[M],tot;
inline void ade(int a,int b,int c,int d){
	to[++tot]=b; w[tot]=d; flow[tot]=c; nex[tot]=head[a]; head[a]=tot;
}
inline void add(int a,int b,int c,int d){
	ade(a,b,c,d); ade(b,a,0,-d);
}
inline void init(){
	tot=1; memset(head,0,sizeof head); t=n; s=n+1;
	for(int i=1;i<=n;i++)	add(i,i+n,1,0);
}
inline int spfa(){
	memset(d,inf,sizeof d);	d[s]=0;	queue<int> q;	q.push(s);
	int vis[N]={0}; vis[s]=1;
	while(q.size()){
		int u=q.front();	q.pop();	vis[u]=0;
		for(int i=head[u];i;i=nex[i]){
			if(flow[i]&&d[to[i]]>d[u]+w[i]){
				d[to[i]]=d[u]+w[i];
				v[to[i]]=u; e[to[i]]=i;
				if(!vis[to[i]])	vis[to[i]]=1,q.push(to[i]);
			}
		}
	}
	return d[t]!=inf;
}
int EK(){
	int res=0;
	while(spfa()){
		int mi=inf;
		for(int i=t;i!=s;i=v[i])	mi=min(mi,flow[e[i]]);
		for(int i=t;i!=s;i=v[i])	flow[e[i]]-=mi,flow[e[i]^1]+=mi;
		if(d[t]<=k)	res+=mi;
	}
	return res;
}
signed main(){
	while(~scanf("%d %d %d",&n,&m,&k),n+m+k){
		init();
		while(m--){
			int a,b;	scanf("%d %d",&a,&b);	add(a+n,b,inf,1);
		}
		printf("%d\n",EK());
	}
	return 0;
}