题意:给一一张有向图。求,删除最少的节点,使从节点1到节点N的距离大于K。
思路:题目中只有50个点,我们可以利用搜索乱搞。。。
我们利用迭代加深搜索来完成整个任务。从小到大枚举删除的点的个数。
对于每一次搜索,我们用BFS来求得节点1到节点N的最短距离,如果最短距离大于K,满足要求,否则,需要在这条最短路上删点,然后继续DFS。
在这道题中,有一个很好的优化,因为删掉的点越多,越可能时距离大于K。所以迭代的深度是个单调函数,可以用二分来优化。
注意:非常要注意的一点是,删点的时候要从距离节点1最近的点开始删,即删点的顺序很重要。因为,距离节点1越近的点,如果删掉,对最短距离影响越大。
错误:卡了很长时间,是因为在每次从头开始搜索的时候,需要对ruin数组进行初始化,因为,我们没有办法保证在上次搜索结束的时候整个ruin数组是初始化好的。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 55;
int head[MAX];
int nxt[5000];
int to[5000];
int tot;
int dist[55];
bool used[55];
bool ruin[55];
int pre[55];
int que[55];
int front, tail;
int N,M,K;
void init()
{
tot = 0 ;
memset(head,-1,sizeof(head));
}
void addedge(int u, int v)
{
to[tot] = v;
nxt[tot] = head[u];
head[u] = tot++;
}
bool bfs(int d)
{
memset(used,0,sizeof(bool)*(N+1));
memset(dist,0x3f,sizeof(int)*(N+1));
front = tail = 0;
dist[1] = 0;
used[1] = true;
que[tail++] = 1;
while(front < tail){
int u = que[front++];
for(int e = head[u]; ~e; e = nxt[e]){
int v = to[e];
if(!used[v] && !ruin[v]){
used[v] = true;
pre[v] = u;
dist[v] = dist[u] + 1;
que[tail++] = v;
}
}
}
return dist[N] > K;
}
bool dfs(int d, int maxd)
{
if(bfs(d)) return true;
if(d == maxd) return false;
int num = 0;
int path[55];
for(int v = pre[N]; v != 1; v = pre[v])
path[num++] = v;
for(int i = num - 1; i >= 0; --i){
int v = path[i];
ruin[v] = true;
if(dfs(d+1,maxd)) return true;
ruin[v] = false;
}
return false;
}
int main(void)
{
//freopen("in","r",stdin);
while(scanf("%d %d %d",&N,&M,&K),N || M ||K){
init();
int u ,v;
for(int i = 0; i < M; ++i){
scanf("%d %d", &u,&v);
addedge(u,v);
}
int lb = -1, ub = N - 2;
while(lb + 1 < ub){
memset(ruin,0,sizeof(bool)*(N+1));
int mid = (lb + ub) >> 1;
if(dfs(0,mid)) ub = mid;
else lb = mid;
}
printf("%d\n",ub);
}
return 0;
}