题目大意:
一个有向图有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, 间谍