bfs

本文介绍了一种针对树形结构的特殊操作——折叠算法。通过特定条件下的路径合并操作,探讨了如何将任意树形结构简化为最短路径的问题。文章提供了一种使用广度优先搜索(bfs)的方法,通过从叶子节点开始逐步向上处理,最终确定是否能够将树转化为一条路径,并求得该路径的最短长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E. Tree Folding
time limit per test2 seconds
memory limit per test512 megabytes
inputstandard input
outputstandard output
Vanya wants to minimize a tree. He can perform the following operation multiple times: choose a vertex v, and two disjoint (except for v) paths of equal length a0 = v, a1, …, ak, and b0 = v, b1, …, bk. Additionally, vertices a1, …, ak, b1, …, bk must not have any neighbours in the tree other than adjacent vertices of corresponding paths. After that, one of the paths may be merged into the other, that is, the vertices b1, …, bk can be effectively erased:

Help Vanya determine if it possible to make the tree into a path via a sequence of described operations, and if the answer is positive, also determine the shortest length of such path.

Input
The first line of input contains the number of vertices n (2 ≤ n ≤ 2·105).

Next n - 1 lines describe edges of the tree. Each of these lines contains two space-separated integers u and v (1 ≤ u, v ≤ n, u ≠ v) — indices of endpoints of the corresponding edge. It is guaranteed that the given graph is a tree.

Output
If it is impossible to obtain a path, print -1. Otherwise, print the minimum number of edges in a possible path.

Examples
input
6
1 2
2 3
2 4
4 5
1 6
output
3
input
7
1 2
1 3
3 4
1 5
5 6
6 7
output
-1
Note
In the first sample case, a path of three edges is obtained after merging paths 2 - 1 - 6 and 2 - 4 - 5.

It is impossible to perform any operation in the second sample case. For example, it is impossible to merge paths 1 - 3 - 4 and 1 - 5 - 6, since vertex 6 additionally has a neighbour 7 that is not present in the corresponding path.

题意:给你一棵树,如果从一个顶点开始存在两条到达叶节点的路径,并且这两条路径的长度相等,且每条路径上除了这个顶点外其它的定点与非该路径的的顶点没有边相连,那么我们可以执行一种操作,这个操作是把这两条路径合并成一条路径,也就是吧两条路径中的其中一条路径删除掉,问经过若干次这种操作之后,这棵树能不能变成一条路径,如果能,输出所有可能的路径中最短的那个路径的长度,如果不能,则输出-1.

解题思路:
我们分析题意可以得出,如果存在,则最后只有一种答案,因为只要满足执行操作的条件,就必须删除其中一条边,不删除的话,最后不可能得到单一路径,而两条边是等价的,所以删除哪条边对最后的结果没有影响,所以题目中那个让你找出最短的边是故意误导你,所以不用管它,只需要判断是否存在,判断的方法是从每个叶节点开始bfs,边删除节点边记录每个顶点能够到达的叶节点的长度,如果最后有某个顶点到达的叶节点的长度有三种或以上,则不存在解。特别需要注意的是储存这棵树时要用set来储存,因为在bfs过程中需要不断的删除边,所以用set比较方便,另外,用set的话会把重复的距离给自动屏蔽掉,便于我们判断。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int n;
set<int> Edge[maxn];//保存每个定点相连的顶点
set<int> Dis[maxn];//保存每个顶点与之能够到达的叶节点的距离
queue<int> Q;
bool visit[maxn];
int cal(int len)
{
    while(len%2 == 0)
    {
        len /= 2;
    }
    return len;
}
int bfs()
{
    memset(visit,false,sizeof(visit));
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if(visit[u]) continue;
        if(Edge[u].size() == 1)//顶点u是叶节点
        {
            if(Dis[u].size() == 1)//只有一个能够到达的叶节点
            {
                int v = *Edge[u].begin();
                Edge[u].erase(v);
                Edge[v].erase(u);
                Q.push(v);
                Dis[v].insert(*Dis[u].begin() + 1);
                visit[u] = true;
            }
        }
        else if(Edge[u].size() == 0)//删除的就剩最后一个顶点
        {
            if(Dis[u].size() >= 3)//无法形成单一路径
            {
                return -1;
            }
            else if(Dis[u].size() == 1)
            {
                return cal(*Dis[u].begin());
            }
            else if(Dis[u].size() == 2)
            {
                return cal(*Dis[u].begin() + *(++Dis[u].begin()));
            }
        }
    }
    return -1;
}
int main()
{
        scanf("%d",&n);
        int u,v;
        while(!Q.empty()) Q.pop();
        for(int i = 1; i <= n - 1; i++)
        {
            scanf("%d%d",&u,&v);
            Edge[u].insert(v);
            Edge[v].insert(u);
        }
        for(int i = 1; i <= n; i++)
        {
            if(Edge[i].size() == 1)
            {
                Q.push(i);//将所有叶节点加入队列
                Dis[i].insert(0);//初始化,为bfs做准备
            }
        }
        printf("%d\n",bfs());
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值