输入包括两行,第一行包括两个正整数n(2 ≤ n ≤ 50)和L(1 ≤ L ≤ 100),表示城市个数和小易能行动的次数。 第二行包括n-1个整数parent[i](0 ≤ parent[i] ≤ i), 对于每个合法的i(0 ≤ i ≤ n - 2),在(i+1)号城市和parent[i]间有一条道路连接。
输出一个整数,表示小易最多能游历的城市数量。
5 2 0 1 2 3
3
#include <stdio.h> #include <string.h> #define MAXN 55 #define MAXM 55 inline void getMax(int& n, int x) { n < x && (n = x); } inline void getMin(int& n, int x) { n > x && (n = x); } struct Edge { int to; int next; } edge[MAXM]; int cnt; int head[MAXN], len[MAXN]; void init() { memset(head, 0xff, sizeof(head)); } void addEdge(int u, int v) { edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt++; } int n, L; void read() { int parent; scanf("%d%d", &n, &L); for (int i = 1; i < n; ++i) { scanf("%d", &parent); addEdge(parent, i); } } void walk(int u) { for (int i = head[u]; ~i; i = edge[i].next) { len[edge[i].to] = len[u] + 1; walk(edge[i].to); } } void work() { walk(0); int maxLen = 0; for (int i = 0; i < n; ++i) { getMax(maxLen, len[i]); } if (L <= maxLen) { printf("%d\n", L + 1); } else { int res = n; getMin(res, maxLen + (L - maxLen) / 2 + 1); printf("%d\n", res); } } int main() { init(); read(); work(); return 0; }
没看见有java语言,我这里添加一个。 思路和一楼基本一致,我的代码建树的过程可以更优化一些。
测试用例:
10 10
0 3 1 3 0 5 2 7 5
建树后,运行完getstep(City city)、检索玩得到max方法后,图的形状和对应的步数。如下图。
代码:
import java.util.ArrayList; import java.util.Scanner; /** * 个人博客 [www.mynight.top](http://www.mynight.top) ,欢迎光顾 * 魔法王国 */ public class MagicCity { static int x = 0; public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); int L = in.nextInt(); City[] citys = new City[n];//从0开始到n均为城市 for(int i=1;i<n;i++)//计数从1开始,自动+1 { int tmp = in.nextInt();//i城市的上一个城市为tmp,tmp下属城市包含i if(citys[i]==null) citys[i] = new City(); if(citys[tmp]==null) citys[tmp] = new City(); citys[i].pre = citys[tmp]; citys[tmp].list.add(citys[i]); } getstep(citys[0]);//将每个结点的step标记。 int max = 0;//记录最常的那条链表 for (int i=0;i<citys.length;i++) { if(citys[i].step>max)max = citys[i].step; } if(max>=L){//判断L是否能走到最常的链表处 System.out.println(L+1); return; } //如果走完max长度后,还有剩余的步数。 int rest = (L-max)/2;//最多还能走的步数(包括返回) //可以很容易的证明,剩下未走过的城市,每多游玩一个城市,就需要花费两步。 int x = n-max-1;//剩下未走过的城市个数 if(rest>=x) System.out.println(n); else{ System.out.println(max+1+rest); } } private static void getstep(City city) { for (int i=0;i<city.list.size();i++) { city.list.get(i).step = city.step+1; getstep(city.list.get(i)); } } static class City{ int step; City pre; ArrayList list = new ArrayList(); } }
题目经过抽象之后,意思是在一个树中进行遍历,经过指定步数,可以获取最长经过节点树量的路径。如果把这个树按照根节点进行悬挂,可能更好理解一些。虽然有些答案是从低向上生长,但是我还是重建了树,采用悬挂树来做的。
从这个根节点开始遍历,先判断左树深度大还是右树深度大,先遍历树深度大的那个节点。直到步数用完为止。
树的深度通过后序遍历很容易求出来,结果发现这样的答案只能通过60%。
45 73 0 0 0 1 0 0 3 5 6 8 7 9 1 10 1 2 15 6 8 11 14 17 8 14 3 21 23 3 21 15 12 5 21 31 11 13 7 17 20 26 28 16 36 26
错在这个用例上了。这个正确答案是41,通过简单的贪心算法只能得到39个城市。
后来看了解析也是看不太懂。总之之后看到正确答案中是求出来深度后直接获得最终答案。
假设我们已经求出了每一个节点的最大深度,用deep[i]来表示,树的最下面一层的深度是1。
显然,根节点到任意一个节点的最长路径=deep[0]-1。
以这条路径为基础,我们可以额外访问一些节点。但是每次访问完这些节点的时候,我们必须回来这个路径。这一来一回,每次访问一个节点都必须额外走两步,访问两个节点就必须走4步。
看图就容易明白一些:
参考代码
#include <vector> #include <iostream> using namespace std; vector<vector<int> > tree; vector<int> deep; void calc_deep(int i) { int max_deep = 0; for(auto j:tree[i]) { calc_deep(j); max_deep = max(deep[j], max_deep); } deep[i] = max_deep + 1; } int main() { int n, L; cin >> n >> L; /* 建立树 */ tree.resize(n); deep.resize(n); for(int i=0;i<n-1;i++) { int num; cin >> num; tree[num].push_back(i+1); } /* 计算深度 */ calc_deep(0); int long_path = deep[0] - 1; if(long_path > L) cout << L + 1; else cout << 1 + long_path + (L - long_path)/2; }
//最简单AC 没有之一#include<iostream> #include<string> #include<vector> #include<algorithm> using namespace std; int main(){ int n; int l; while (cin >> n){ cin >> l; vector<int> v1(n, 0); v1[0] = 1; int temp; int res = 1; for (int i = 0; i < n - 1; i++){ cin >> temp; v1[i+1] = v1[temp]+1; if (v1[i + 1]>res){ res = v1[i + 1]; } } if (res < l + 1){ int res_temp = res + (l - res + 1) / 2; if (res_temp > n){ cout << n << endl; } else{ cout << res + (l - res + 1) / 2 << endl; } } else{ cout << l + 1 << endl; } } return 0; }
import sys n,l=list(map(int,sys.stdin.readline().strip().split())) parent=list(map(int,sys.stdin.readline().strip().split())) deepLen=[0]*n for i in range(n-1): deepLen[i+1]=deepLen[parent[i]]+1 maxLen=max(deepLen) if l<maxLen: print(l+1) else: print(maxLen+1+(l-maxLen)//2)
语言:C++ 运行时间: 4 ms 占用内存:504K 状态:答案正确
思路参考的@夭寿啦要没Offer啦,但是我使用的简单的邻接矩阵存的树,编写和理解都很简单,height函数是用来求树高度,求出树高其他的就非常简单了。
本套8道题的C++代码已挂到了我的GitHub(https://github.com/shiqitao/NowCoder-Solutions)上,持续更新。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
int height(bool adj[], int node);
int main()
{
int l; cin >> n >> l;
bool *adj = new bool[n*n];
memset(adj, false, sizeof(bool)*n*n);
int temp;
for (int i = 1; i < n; i++) {
cin >> temp;
adj[temp*n + i] = true;
}
int h = height(adj, 0);
cout << min(min(l + 1, (l + 1 - h) / 2 + h), n);
return 0;
}
int height(bool adj[], int node)
{
int maxLen = 0;
for (int i = 0; i < n; i++) {
if (adj[node*n + i]) {
int temp = height(adj, i);
maxLen = max(maxLen, temp);
}
}
return maxLen + 1;
}
//其实就是求树最大深度 import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class Main { static int[] arr = null; public static int maxdeep(int root) {//获取最大深度 List<Integer> list = new ArrayList<>();// 获取root节点的所有子节点、list为空,没有子节点 for (int i = 0; i < arr.length; i++) { if (arr[i] == root) { System.out.println(root); list.add(i + 1); } } if (list.isEmpty()) return 0; else { int max = 0;// 子数的最大深度 //获取root所有子节点中的最大深度 for (Integer integer : list) { int temp = maxdeep(integer); if (max < temp) max = temp; } return 1 + max; } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); int l = scanner.nextInt(); arr = new int[n - 1]; for (int i = 0; i < n - 1; i++) { arr[i] = scanner.nextInt(); } int max = Main.maxdeep(0) + 1;// 不分叉最多走的节点 int other = n - max;// 剩余节点 if (l <= max - 1) System.out.println(l + 1); else System.out.println((l - max + 1) / 2 < other ? (l - max + 1) / 2 + max : other + max); } } }
import java.util.ArrayList; import java.util.Scanner; /** * 魔法王国一共有n个城市,编号为0~n-1号,n个城市之间的道路连接起来恰好构成一棵树。 小易现在在0号城市,每次行动小易会从当前所在的城市走到与其相邻的一个城市,小易最多能行动L次。 如果小易到达过某个城市就视为小易游历过这个城市了,小易现在要制定好的旅游计划使他能游历最多 的城市,请你帮他计算一下他最多能游历过多少个城市(注意0号城市已经游历了,游历过的城市不重复计算)。 *输入有两行,第一行的两个整数分别为n个城市和可以走L次 *第二行为n-1个整数 0=<parent[i] <= i+1; parent[i] 与 i+1之间有一条道路连接 * 0<=i <n-2 * 5 2 0 1 2 3 * */ public class TripMax { public static void getStep(City city){ for(int i = 0;i < city.list.size();i++){ city.list.get(i).step = city.step +1; //子节点的步数等于当前父节点的步数加1 getStep(city.list.get(i)); //有子节点的话继续回调 } } public static void main(String[] args) { // TODO Auto-generated method stub Scanner scan = new Scanner(System.in); int n = scan.nextInt(); //城市数 int L = scan.nextInt(); //行走次数 City[] citys = new City[n]; for(int i = 1;i<n;i++){ //从1开始 int temp = scan.nextInt(); //i与parent[i-1]有一条道路连接 if(citys[i] == null){ citys[i] = new City(); } if(citys[temp] == null){ citys[temp] = new City(); } //citys[i].pre = citys[temp]; citys[temp].list.add(citys[i]); } //计算每一个城市节点的步数,因为一开始在城市0,所以从citys[0]开始 getStep(citys[0]); int maxLen = 0; //城市树的最长步数 for(int j = 1;j <n; j++){ if(citys[j].step > maxLen){ maxLen = citys[j].step; } } if(maxLen >= L){ //如果行走次数小于或等于最长步数,则直接输出 L+1(1是指一开始已游历的0号城市) System.out.println( L+1); return ; } //否则的话,则有以下两种情况, 根据二叉树的结构,每多走一个城市就要多花费两步 int rest = (L - maxLen)/2 ; //剩下可行走的次数还可以走多少个城市 int remainingCity = n - maxLen - 1; //还没有游历过的城市个数 //1.如果还可以游历的城市的个数大于或等于没有有游历过的城市个数,则小易可以把所有城市都游历完。 if(rest > remainingCity){ System.out.println(n); return; }else{ //2.否则,小易最多可以游历 (maxLen+rest+1)个城市 System.out.println(maxLen+rest+1); return; } } static class City{ int step; //City pre; ArrayList<City> list = new ArrayList<City>(); } }
#include<stdio.h> #include<vector> #include<set> #include<string.h> using namespace std; vector<int> tree[105]; int Max,n,l; void dfs(int,int); int main(){ int i; //freopen("input.txt","r",stdin); while(scanf("%d%d",&n,&l)!=EOF){ for(i=0;i<105;i++) tree[i].clear(); Max=0; for(i=0;i<n-1;i++){ int x; scanf("%d",&x); tree[x].push_back(i+1); } dfs(0,0); if(Max>=l) printf("%d\n",l+1); else printf("%d\n",1+(l-Max)/2+Max); } } void dfs(int x,int step){ if(Max<step) Max=step; for(int i=0;i<tree[x].size();i++) dfs(tree[x][i],step+1); }
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();//城市的个数
int L = sc.nextInt();//移动次数
int maxLength = 0;
int[] parent = new int[n];
int[] depth = new int[n];
for(int i = 1; i <= n - 1; i++){
parent[i] = sc.nextInt();
depth[i] = depth[parent[i]] + 1;//当前城市节点的深度等于父节点的深度加1
if(depth[i] > maxLength) maxLength = depth[i];
}
if(maxLength >= L)System.out.println(L + 1);
//如果L比最长路径大,那么最长的路径要最后走,前面无论怎么走其他分支,多走一个城市就需要2步(一来一回)。
else System.out.println((L - maxLength) / 2 + maxLength + 1);
}
}
#include<iostream> #include<vector> using namespace std; int main(){ int n,L; while(cin>>n>>L){ vector<int> p(n,0); p[0]=1; int mDepth=p[0]; int temp; for(int i=1;i<n;i++){ cin>>temp; p[i]=p[temp]+1;//i号城市在树中的深度 if(p[i]>mDepth) mDepth=p[i];//树的最大深度 } if(L<mDepth)//行动步数小于树的最大深度 cout<<L+1<<endl; else{ int temp=mDepth+(L+1-mDepth)/2;//行动步数大于树的最大深度,有些城市需要往返 if(temp>=n)//行动步数足以遍历所有的城市 cout<<n<<endl; else cout<<temp<<endl; } } return 0; }
import java.util.Scanner; public class Main{ public static void main(String[] args) { Scanner scan = new Scanner(System.in); int n = scan.nextInt(), L = scan.nextInt(); int parent[] = new int[n]; int depth[] = new int[n]; int maxDepth = 0; for (int i = 1; i < n; i++) { parent[i] = scan.nextInt(); depth[i] = depth[parent[i]] + 1; if (depth[i] > maxDepth) maxDepth = depth[i]; } scan.close(); int count = 0; if (maxDepth >= L) count = L; else count = (L - maxDepth) / 2 + maxDepth; if(count >= n - 1) count = n - 1; System.out.println(count + 1); } }
#include<iostream> using namespace std; int main() { int city,act,arr[51] = {0}, temp, max=0; cin>>city>>act; for(int i = 1; i < city; ++i) { cin>>temp; arr[i] = arr[temp]+1; if(arr[i]>max) max = arr[i]; } if(max>=act) cout<<act+1; else { cout<<(act-max)/2+1+max; } return 0; }
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(), L = sc.nextInt(); int[] cities = new int[n]; for(int i = 1; i < n; i++){// 树的结构不一定从顶端顺序依次构建, 先初始化数据, 后续处理 cities[i] = sc.nextInt(); //i为几号城市, cities[i] 为他的父节点 } int maxDept = getMaxLevel(cities, 0, 1);//获取最大节点 /* * 总的来说两种情况 * 1. L行动次数比最大深度小 * 2. L行动次数比最大深度大,其余每个城市去(除0号城市),访问的代价是2个行动步数, 但不能超过可以访问的城市数量。 * */ int result = (L < maxDept) ? L + 1 : Math.min(n, maxDept + (L - maxDept + 1) / 2); System.out.println(result); } /** * 查询最大深度 * @param cities * @param num * @param currLevel * @return */ public static int getMaxLevel(int[] cities, int num, int currLevel){ int maxLevel = currLevel; for(int i = 1; i < cities.length; i++){ if (cities[i] == num){ maxLevel = Math.max(maxLevel, getMaxLevel(cities, i, currLevel + 1)); } } return maxLevel; } }
n,steps=input().split(' ')
n,steps=int(n),int(steps)
links=input().split(' ') #连接关系
depth_every=[0 for x in range(n)] #每一个城市的深度
depth_every[0]=1 #0城市的深度
max_depth=1 #树最大深度
for i in range(n-1):
tmp=int(links[i])
new_depth=depth_every[tmp]+1
depth_every[i+1]=new_depth
max_depth=max(max_depth,new_depth)
if max_depth<steps+1:
#可以走的步数大于树最大深度,此时必然选择先走短分支,最后走最长分支
#走短分支必然需要返回操作,所以扣除最长分支max_depth的步数除以2取整,
#表示额外到达的城市
tmp=max_depth+int((steps+1-max_depth)/2)
print(min(tmp,n))
else:
print(steps+1)