题目的大意是给你一棵树 ,相连节点之间的距离为一,让你找出一个路线访问到k个不同节点 (一条边在这个路径中可以被走多次),问你这样这个路径的距离最小值是多少?
转化之后其实就是要找出数的直径即任意2点间的最大距离,一种方法是任取一个节点做一次广搜找出其最长的子树,然后以该子树的最远的叶子上的节点作为起点再做一次广搜之后得到的最长子树的长度就是树的直径了
对于要访问的点的个数k 小于等于树的直径上的点的数量则答案为k-1
大于的时候 就要考虑怎么走了?肯定走最长路是不够的,但因为最长路的性价比高还是要走最长路,但最长路之外的距离都应该变成2,因为要访问非最长路的点后还得沿原路返回到最长路上来,故答案就是 (k-malen-1)*2+malen = 2*k-malen-2 其中malen为树的直径 ,k-malen-1 是除了数的直径上的点外还需要访问的点个数
#include<cstdio> #include<vector> #include<cstring> #include<vector> #include<queue> using namespace std; typedef vector<int>::iterator IntVcIt; queue<int> que; vector<vector<int> > tu(100005); bool vis[100005]; int m=0,n,malen,len[100005]; void read() { for(int i=1;i<=m;i++) tu[i].clear(); scanf("%d%d",&m,&n); for(int i=1;i<m;i++) { int a,b;scanf("%d%d",&a,&b); tu[a].push_back(b); tu[b].push_back(a); } } int bfs(int start=1)//广搜,默认的起点为1 { memset(vis,0,sizeof(vis));//标记该点有没被访问过 memset(len,0,sizeof(len));//初始化从起点到每个点的距离 que.push(start);//起点入队 int max_start=1;//最长路径的非起点的端点 malen=0;//树的直径 while(!que.empty()) { int temp=que.front();que.pop(); for(IntVcIt it=tu[temp].begin();it!=tu[temp].end();++it) if(!vis[*it]) { len[*it]=len[temp]+1; if(len[*it]>malen) { malen=len[*it]; max_start=*it; } que.push(*it); } vis[temp]=1; } return max_start;//返回最长子树的非起点的端点 } void deal() { bfs(bfs());//2次广搜 for(int i=0;i<n;i++) { int a;scanf("%d",&a); if(a<=malen+1) printf("%d\n",a-1); else printf("%d\n",2*a-malen-2); } } int main() { int T;scanf("%d",&T); while(T--) { read(); deal(); } }