城市巡游赛(树和图)

[问题描述]

有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

以上便是我对这道题的看法,很高兴与大家分享。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构课程设计数据结构

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值