[问题描述]
有N个城市正在举办厨艺大赛,这些城市的编号为1~N,城市之间有N-1条道路相连,并且城市之间两两可达。每个城市i都有一个里程值Ai:从城市X出发,要达到城市Y的必要条件是存在一条从X到Y的简单路径,且该路径恰好有Ax条边。
为了尽可能多地参加各地比赛,假设你可以从任意城市出发,且可以访问任意城市多次,求最多可以参加多少场比赛。
严格来说,给定一棵树,其中每个节点有权值Ai,如果我们可以从任意节点出发,并经过任意节点多次,你需要求出最多可以到达的城市数量,唯一条件是从i移动到j当且仅当i到j的简单路径上恰好有Ai条边。
[输入格式]
+ 第一行包含N
+ 接下来N-1行包含u,v表示一条边
+ 接下来一行包含N个整数Ai
[输出格式]
输出最多可以访问多少不同的城市
[补充说明]
+ 1≤ui≤N,1≤vi≤N,且ui≠vi
+ 保证输入是树
[样例1]
输入:
5
1 3
3 5
1 4
4 2
1 2 3 3 1
输出:
5
[样例2]
输入:
8
1 5
1 6
1 7
1 8
1 2
2 3
3 4
1 2 3 4 5 6 7 8
思考与解释:
首先解释一下题目的意思:N个城市,城市之间有N-1条道路相连,并且城市之间两两可达。若把它视为一张图,则该图连通且 边数=顶点数 - 1,可以得出该图连通且不含回路(根据离散数学中的知识,这里不多加解释),即该图为树。简单路径则是指通路中所有边互不相同。举例:假设A1点的里程值为2,则A1可以到达的城市必须和A1有一条包含两条边的简单路径。
解题方法:
1、求出每个点可以到达的城市。我们先构建原图(由于树也是图的一种,这里我们统一用图来处理),再利用BFS,限定广搜的范围(即搜到第几条边停下),在最后一次搜索时,遍历到的点即为可以到达的点。
2、构建新图。在可以到达的城市间建立边(有向,因为有A1->A2,但不一定有A2->A1),这样就形成了一张新图(注意新图不一定所有点都连通)。
3、在新图中找出包含点数目最多的单向连通图(有向图D中任意两个顶点至少一个可达另一个,则称D是单向连通图)。这个单向连通图包含的顶点数即为最终结果。依次从各点出发,采用深度优先遍历,把经过的边删掉,当一个顶点无法继续向下遍历时,将此时已经过的顶点数与res比较,取最大值。最后的res即为最终结果。
程序代码:
# include <iostream>
# include <stdlib.h>
# include <algorithm>
# include <queue>
# define SIZE 1024
using namespace std;
typedef struct ArcNode { //边的结点结构类型
int adjvex; //该边的终点编号
struct ArcNode* nextarc; //指向下一条边的指针
}ArcNode;
typedef struct VexNode { //顶点结构
int num; //顶点序号
int x; //顶点的里程值
ArcNode* firstarc; //指向第一条与该顶点有关的边的指针
}VexNode;
typedef struct Graph { //邻接表结构类型
VexNode* VNode; //定义邻接表
int vexnum, arcnum; //顶点数和边的个数
int type; //图的种类(1:无向图 2:有向图)
}Graph;
int res = 0; //最终结果
int Visit[SIZE] = { 0 }; //辅助数组,标记点是否已被访问过
int A[SIZE] = { 0 }; //各点的里程值
int Arc[SIZE][2]; //边
int newarc = 0; //新图的边数
void CreateGraph(Graph& G); //创建图
void BFS(Graph G1, int n, int m); //n:顶点编号 m:广搜的深度
void BFSGraph(Graph G1); //对G1广度优先遍历,求出每个顶点可以到达的城市
void DFS(Graph G2, int n, int m); //n:顶点编号 m:经过点的数目
void DFSGraph(Graph G2); //对G2深度优先遍历,求出从每个顶点出发最多可以到达的城市数
int main()
{
Graph G1, G2; //G1是原图,G2是新图
G1.type = 1; //G1是无向图
cin >> G1.vexnum;
G1.arcnum = G1.vexnum - 1;
for (int i = 1; i <= G1.arcnum; i++){
//输入边
cin >> Arc[i][0] >> Arc[i][1];
}
for (int i = 1; i <= G1.vexnum; i++){
//输入各点的里程值
cin >> A[i];
}
CreateGraph(G1); //构建G1
//重新初始化
for (int i = 1; i <= G1.arcnum; i++){
Arc[i][0] = 0;
Arc[i][1] = 0;
}
for (int i = 1; i <= G1.vexnum; i++){
A[i] = 0;
}
BFSGraph(G1); //求G2的边
G2.type = 2; //G2是有向图
G2.vexnum = G1.vexnum;
G2.arcnum = newarc;
CreateGraph(G2); //构建G2
DFSGraph(G2);
cout << res << endl;
return 0;
}
//创建图
void CreateGraph(Graph& G)
{
G.VNode = (VexNode*)malloc(SIZE * sizeof(VexNode));
for (int i = 1; i <= G.vexnum; i++){
//邻接表初始化,所有单向链表均为空表,同时初始化里程值
G.VNode[i].firstarc = NULL;
G.VNode[i].x = A[i];
}
int x, y; //x:起始点,y:终点
ArcNode* p, * q;
for (int i = 1; i <= G.arcnum; i++){
x = Arc[i][0];
y = Arc[i][1];
p = (ArcNode*)malloc(sizeof(ArcNode)); //创建一个用于存放当前边的结点p
p->nextarc = NULL;
p->adjvex = y;
q = G.VNode[x].firstarc;
//将边按顺序插入到链表末尾
if (q == NULL){
G.VNode[x].firstarc = p;
}
else{
while (q->nextarc != NULL){
q = q->nextarc;
}
q->nextarc = p;
}
if (G.type == 1){
//如果是无向图,要再创建一个表示对称边的结点p
p = (ArcNode*)malloc(sizeof(ArcNode));
p->nextarc = NULL;
p->adjvex = x;
q = G.VNode[y].firstarc;
if (q == NULL){
G.VNode[y].firstarc = p;
}
else{
while (q->nextarc != NULL){
q = q->nextarc;
}
q->nextarc = p;
}
}
}
}
//n:顶点编号 m:广搜的深度
void BFS(Graph G1, int n, int m)
{
queue<int>Q;
Visit[n] = 1; //将起始点标记为已被访问过的状态
Q.push(n); //起始点入队
int k = 0; //追踪遍历的范围
int s1 = 1, s2 = 0;//s1:当前层元素个数 s2:下一层元素个数
int a; //跟踪顶点
while (!Q.empty()){
if (k == m){
//已达到范围
break;
}
while (!Q.empty() && s1){
a = Q.front(); //队头元素出队
Q.pop();
ArcNode* p = G1.VNode[a].firstarc;
while (p){
//遍历当前队头结点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
//否则入队,并标记为已访问状态,防止重复入队
if (!Visit[p->adjvex]){
if (k == m - 1){
newarc++;
Arc[newarc][0] = n;
Arc[newarc][1] = p->adjvex;
}
Visit[p->adjvex] = 1;
Q.push(p->adjvex);
s2++;
}
p = p->nextarc;
}
s1--;
}
k++;
s1 = s2;
s2 = 0;
}
}
//对G1广度优先遍历,求出每个顶点可以到达的城市
void BFSGraph(Graph G1)
{
for (int i = 1; i <= G1.vexnum; i++){
//辅助数组初始化
for (int j = 1; j <= G1.vexnum; j++){
Visit[j] = 0;
}
//对每一个顶点均调用一次广度优先遍历,限定范围为顶点的里程值
BFS(G1, i, G1.VNode[i].x);
}
}
//n:顶点编号 m:经过点的数目
void DFS(Graph G2, int n, int m)
{
int k, t = 1;
ArcNode* p = G2.VNode[n].firstarc;
while (p){
//遍历起始点的所有邻接点,若邻接点已被访问过,则p继续向后遍历
//否则,以当前邻接点为新的起始点递归进行深度优先遍历
if (p->adjvex > 0){
t = 0;
k = p->adjvex;
if (!Visit[k]){
Visit[k] = 1;
//把经过的边删除
p->adjvex = 0;
DFS(G2, k, m + 1);
p->adjvex = k;
Visit[k] = 0;
}
else{
//把经过的边删除
p->adjvex = 0;
DFS(G2, k, m);
p->adjvex = k;
}
}
p = p->nextarc;
}
if (t){
res = max(res, m);
}
}
//对G2深度优先遍历,求出从每个顶点出发最多可以到达的城市数
void DFSGraph(Graph G2)
{
for (int i = 1; i <= G2.vexnum; i++){
//辅助数组初始化
for (int j = 1; j <= G2.vexnum; j++){
Visit[j] = 0;
}
//对每一个顶点均调用一次深度优先遍历
Visit[i] = 1;
DFS(G2, i, 1);
}
}
运行示例:
8
1 5
1 6
1 7
1 8
1 2
2 3
3 4
1 2 3 4 5 6 7 8
4
以上便是我对这道题的看法,很高兴与大家分享。