OJ:https://cn.vjudge.net/problem/HDU-3069
这个题还挺有意思的,思路感觉比较奇特,题目的大概意思是,有n个城市,这些城市两两之间只存在一条可以连通的没有方向路径(说明这是一棵树嘛),现在有一个犯人逃跑了,问最少需要派出多少警察能把这个犯人抓住,当警察和犯人在一个城市或者一条路上的时候,犯人会逮捕,移动速度是一样的。
刚开始看完了很是懵逼,其实就是让警察把犯人堵的无处可逃,问最少需要多少警察。
因为给定的这个图是一个树的结构,当但是是一个没有根的树,我们需要找一个根节点,让所有的警察从根节点出发去围堵这些犯人,如果一个节点有两个子节点,为了避免犯人在这三个点之间逃窜,当前父节点就需要一个警察蹲守。
如果一个节点有两个以上的子节点,那么需要的警察数就是这些子节点中需要的警察数的最多的那一个。但是当存在多个子节点需要的警察数是与最大值相同的,那么当前父节点需要的警察数就要加1。
因为,如果只有一个子节点需要的警察数与最大值相同,那么只利用这个子节点的警察就能围堵到当前父节点所有的子节点了,围堵其他子节点的时候一个警察驻守在父节点,因为当前的警察数是所有的子节点中需要警察数最多的哪一个,所以去除了这个驻守的警察,剩下的警察也能围堵那些子节点,然后其他子节点都围堵完了剩下的这个需求警察数最多的让所有的警察都去围堵就可以了。
如果有多个节点的需要的警察数与最大值相同,那么当前父节点就需要一个额外的警察来驻守了,因为要防止犯人去这些节点之间逃窜。
代码实现:
在写代码的时候,因为是树,属于稀疏图,刚开始的时候想用链表去表示图,但是可能是因为测试数据太多了,链表空间没有复用,导致内存超了,后面注意最大的边的数量其实是已知的,所以就选择了将所有的边空间直接分配了,然后把一个节点连接的所有的边放到一个链表上,就可以了。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 1005
#define max(x,y) x > y ? x : y
typedef struct{
int from,to;
}Edge;
Edge edges[MAX*2];
int cityEdge[MAX];
int currentEdgeId;
// 这是一个List,与题无关,如果语言有容器可以用容器代替
typedef struct{
int size;
int num[100];
}List;
void push(List *list,int num){
list->num[list->size++] = num;
}
int dfs(int cur,int pre){
int m = 0;
List list;
list.size = 0;
for(int i=cityEdge[cur];i!=-1;i = edges[i].from){
Edge e = edges[i];
// 下一个点如果是前一个节点
if(e.to == pre){
continue;
}
int r = dfs(e.to,cur);
push(&list,r);
m = max(r,m);
}
if(list.size == 0){
return 1;
}
int n = 0;
// 如果与最大值相同的不止一个,说明当前节点需要有人蹲守
for(int i=0;i<list.size;i++){
if(list.num[i] == m){
n++;
}
}
if(n > 1){
return m+1;
}
return m;
}
void addEdge(int from,int to){
edges[currentEdgeId].to = to;
// 将当前from点所有的路径连接成一个链表,反向链表
edges[currentEdgeId].from = cityEdge[from];
// cityEdge[from]保存的是当前from这点连接到了哪个路径
cityEdge[from] = currentEdgeId++;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
memset(cityEdge,-1,sizeof(cityEdge));
currentEdgeId = 0;
for(int i=0;i<n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
addEdge(x,y);
addEdge(y,x);
}
printf("%d\n",dfs(1,0));
}
return 0;
}