POJ 3921 Destroying the bus stations

题目大意:

        一个有向图有n个顶点(0 < n ≤ 50,编号为1 - n),m条边(0 < m ≤ 4,000),两个顶点之间可以有多条边,现要求删除最少的点使得从1号到n号顶点不存在长度≤k的路径(0 < k < 1,000),但是不能直接删掉1号和n号点。

        现有多个测例,每个测例中都给出n、m、k,对于m条边都给出每条边的起始点序号和终止点序号,测例以n、m、k都等于0结束,对于每个测例都要求输出最少删除的点数。

题目链接

注释代码:

/*                                               
 * Problem ID : POJ 3921 Destroying the bus stations
 * Author     : Lirx.t.Una                                               
 * Language   : C++                              
 * Run Time   : 110 ms                                               
 * Run Memory : 152 KB                                               
*/ 

#include <memory.h>
#include <stdio.h>

#define	TRUE		1
#define	FALSE		0

//maximum number of bus stations
//公交站的最大数量
#define	MAXSTAN		51
//maximum number of roads
//公交站之间通路的最大数量
#define	MAXRODN		4001

typedef	char	BOOL;

//图的邻接表
short	hd[MAXSTAN];//head
short	ac[MAXRODN];//arc
short	nx[MAXRODN];//next
short	e;//edge

//BFS广搜出一条从1到n的最短路径
//由于每条边长都是1
//所以没有必要Dij或者SPFA
//广搜到的第一条通路即为1到n的最短路径
char	q[MAXSTAN];//queue
BOOL	vist[MAXSTAN];//visited

BOOL	dl[MAXSTAN];//deleted,dl[i]用于判断i点是否被删除
char	ovl[MAXSTAN];//overlap,表示i点的重叠层数,用于剪枝
char	prv[MAXSTAN][MAXSTAN];//用于记录BFS搜索出的最短路径
//prv[depth][i]表示在第depth层中BFS搜出的路径中i点的前驱结点编号

char	botm;//迭代深搜的界限bottom

int		n, m, k;

void
addarc( int u, int v ) {

	nx[e] = hd[u];
	ac[e] = v;
	hd[u] = e++;
}

int
len(char *prv) {//计算找出的路径的长度

	int		u;//临时变量,指向一个点
	int		cnt;//计数变量,记录路径长度

	cnt = 0;
	u   = n;

	while ( u != 1 ) {
	
		u = prv[u];
		cnt++;
	}

	return cnt;
}

BOOL
bfs(char *prv) {//寻找一条最短路径

	int		front, tail;

	int		u, v;
	int		i;

	memset(vist, 0, sizeof(vist));
	vist[1] = TRUE;

	q[0]  = 1;
	front = 0;
	tail  = 1;

	while ( front < tail ) {

		u = q[front++];
		for ( i = hd[u]; i; i = nx[i] )
			if ( !vist[ v = ac[i] ] && !dl[v] ) {//注意!!
				//还要看一下该店是否在dfs中删除了!!!
			
				prv[v] = u;
				if ( v == n )
					goto s;

				vist[v]   = TRUE;
				q[tail++] = v;
			}
	}
	return TRUE;//表示1到n不可达,即1到n的距离无穷大,因此一定大于k,所以成功退出

s:	if ( len(prv) > k )//如果最短路径存在,计算其长度是否满足题中大于k的要求
		return TRUE;
	
	return FALSE;
}

BOOL
dfs(int dp) {//迭代深搜,DFSID
	//depth,表示迭代的深度
	//这里就表示当前要删掉第几个点(第dp个点)

	if ( dp > botm )//超出界限则失败退出
		return FALSE;

	if ( bfs( prv[dp] ) )//如果在当前深度中存在>k的最短路径则成功退出
		return TRUE;

	int		u;//临时变量,指向点的指针

	for ( u = prv[dp][n]; u != 1; u = prv[dp][u] ) {//遍历枚举当前层次中
		//最短路径中的每个点
		//枚举的顺序是从n到1
	
		if ( !ovl[u] ) {//!!!重要的剪枝
			//如果u在上面几层中被试探过了
			//就不必删除,可以直接忽略
			//即如果有原图中有部分路径相互重叠,如果上层搜索过重叠部分,则下层可以不必检验
			//可以使算法大大加速
			//具体证明不详!

			dl[u] = TRUE;//先删掉
			if ( dfs( dp + 1 ) )//然后在下一层中找一条最短路径检验是否合格
				return TRUE;
			dl[u] = FALSE;//检验完后再枚举上一个点,将u点还原!
		}
		
		ovl[u]++;//将u的重叠值+1
	}

	for ( u = prv[dp][n]; u != 1; u = prv[dp][u] )
		ovl[u]--;//当一层搜索完了就将所有重叠值-1,表示退出这1层,回溯到上层
	//使上层的ovl值保持为原样

	return FALSE;//没有搜出想要的结果,因此失败退出回溯到上层
}

int
main() {

	int		u, v;

	while ( scanf("%d%d%d", &n, &m, &k), n ) {
	
		memset(hd, 0, sizeof(hd));

		e = 1;
		while ( m-- ) {
		
			scanf("%d%d", &u, &v);
			addarc( u, v );
		}

		for ( botm = 0; botm <= n - 2; botm++ ) {
			//表示先删0个点,再试一试只删1个点,再试只删2个点...
			//1和n不能删
			//第一次碰到的可行的结果就是最终答案
		
			//将删点信息和重叠信息初始化(清空)
			memset(dl, 0, sizeof(dl));
			memset(ovl, 0, sizeof(ovl));

			if ( dfs(0) ) {//从0个点删到botm个点为止
			
				printf("%d\n", botm);
				break;
			}
		}
	}

	return 0;
}

无注释代码:

#include <memory.h>
#include <stdio.h>

#define	TRUE		1
#define	FALSE		0

#define	MAXSTAN		51
#define	MAXRODN		4001

typedef	char	BOOL;

short	hd[MAXSTAN];
short	ac[MAXRODN];
short	nx[MAXRODN];
short	e;

char	q[MAXSTAN];
BOOL	vist[MAXSTAN];

BOOL	dl[MAXSTAN];
char	ovl[MAXSTAN];
char	prv[MAXSTAN][MAXSTAN];

char	botm;

int		n, m, k;

void
addarc( int u, int v ) {

	nx[e] = hd[u];
	ac[e] = v;
	hd[u] = e++;
}

int
len(char *prv) {

	int		u;
	int		cnt;

	cnt = 0;
	u   = n;

	while ( u != 1 ) {
	
		u = prv[u];
		cnt++;
	}

	return cnt;
}

BOOL
bfs(char *prv) {

	int		front, tail;

	int		u, v;
	int		i;

	memset(vist, 0, sizeof(vist));
	vist[1] = TRUE;

	q[0]  = 1;
	front = 0;
	tail  = 1;

	while ( front < tail ) {

		u = q[front++];
		for ( i = hd[u]; i; i = nx[i] )
			if ( !vist[ v = ac[i] ] && !dl[v] ) {
			
				prv[v] = u;
				if ( v == n )
					goto s;

				vist[v]   = TRUE;
				q[tail++] = v;
			}
	}
	return TRUE;

s:	if ( len(prv) > k )
		return TRUE;
	
	return FALSE;
}

BOOL
dfs(int dp) {

	if ( dp > botm )
		return FALSE;

	if ( bfs( prv[dp] ) )
		return TRUE;

	int		u;

	for ( u = prv[dp][n]; u != 1; u = prv[dp][u] ) {
	
		if ( !ovl[u] ) {

			dl[u] = TRUE;
			if ( dfs( dp + 1 ) )
				return TRUE;
			dl[u] = FALSE;
		}
		
		ovl[u]++;
	}

	for ( u = prv[dp][n]; u != 1; u = prv[dp][u] )
		ovl[u]--;

	return FALSE;
}

int
main() {

	int		u, v;

	while ( scanf("%d%d%d", &n, &m, &k), n ) {
	
		memset(hd, 0, sizeof(hd));

		e = 1;
		while ( m-- ) {
		
			scanf("%d%d", &u, &v);
			addarc( u, v );
		}

		for ( botm = 0; botm <= n - 2; botm++ ) {
		
			memset(dl, 0, sizeof(dl));
			memset(ovl, 0, sizeof(ovl));

			if ( dfs(0) ) {
				
				printf("%d\n", botm);
				break;
			}
		}
	}
	
	return 0;
}

单词解释:

barrack:n, 营房,兵营

military:adj, 军事的,军用的

vehicle:n, 车辆

ban:vt, 禁止

mission:n, 使命,任务

spy:n, 间谍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值