D~F Java题解,代码已去除冗余~~~
换根动态规划,记录每个节点子树的最大叉数,以及子节点的前二大叉数,两次深搜即可,时间复杂度O(n)
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),max[][]=new int[n+5][3];
List<Integer> path[]=new List[n+5];
for(int i=1;i<=n;i++){
path[i]=new ArrayList<>();
}
for(int i=0;i<n-1;i++){
int u=sc.nextInt(),v=sc.nextInt();
path[u].add(v);
path[v].add(u);
}
find(1,0,path,max);
int ans[]=find(1,0,0,path,max);
System.out.println(ans[0]+" "+ans[1]);
}
static int[] find(int k,int p,int pre,List<Integer> path[],int max[][]){
int ans[]=new int[]{Math.max(pre,Math.max(max[k][0],path[k].size())),k};
for(int a:path[k]){
if(a!=p){
int cur[]=find(a,k,Math.max(Math.max(pre,path[k].size()-1),max[a][0]==max[k][1]?max[k][2]:max[k][1]),path,max);
if(cur[0]<ans[0]||cur[0]==ans[0]&&cur[1]<ans[1]){
ans=cur;
}
}
}
return ans;
}
static void find(int k,int p,List<Integer> path[],int max[][]){
int count=0;
for(int a:path[k]){
if(a!=p){
count++;
find(a,k,path,max);
max[k][0]=Math.max(max[k][0],max[a][0]);
int arr[]=new int[]{max[k][1],max[k][2],max[a][0]};
Arrays.sort(arr);
max[k][1]=arr[2];
max[k][2]=arr[1];
}
}
max[k][0]=Math.max(max[k][0],count);
}
}
根据出题人的解答,无需建树,可以大大简化思路:
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),count[]=new int[n+5],max=0;
for(int i=0;i<n-1;i++){
count[sc.nextInt()]++;
count[sc.nextInt()]++;
}
for(int a:count){
max=Math.max(max,a);
}
for(int i=1;i<=n;i++){
if(count[i]!=max){
System.out.println(max-1+" "+i);
return;
}
}
System.out.println("1 1");
}
}
E 智乃的“凑数”题(Easy Version) && F 智乃的“凑数”题(Hard Version)
类似于01背包的思路,需要分别记录两部分分别的和,只需要记录乘积在10000以下的状态即可,总共最多10000log10000
个不同状态,同时记录每个可达状态的来源即可,时间复杂度(nClogC+m(n+sqrt(x)),C==10000
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt(),pre[][][]=new int[10005][][];
boolean has[][]=new boolean[10005][];
for(int i=0;i<=10000;i++){
has[i]=new boolean[10000/Math.max(1,i)+5];
pre[i]=new int[10000/Math.max(1,i)+5][];
}
has[0][0]=true;
for(int i=0;i<n;i++){
int k=sc.nextInt();
boolean temp[][]=new boolean[10005][];
for(int j=0;j<=10000;j++){
temp[j]=has[j].clone();
}
for(int j=0;j<=10000;j++){
for(int p=10000/Math.max(j,1);p>=0;p--){
if(j>=k&&!temp[j][p]&&has[j-k][p]){
temp[j][p]=true;
pre[j][p]=new int[]{j-k,p};
}
if(p>=k&&!temp[j][p]&&has[j][p-k]){
temp[j][p]=true;
pre[j][p]=new int[]{j,p-k};
}
}
}
has=temp;
}
for(int i=0;i<m;i++){
find(sc.nextInt(),has,pre);
}
}
static void find(int x,boolean has[][],int pre[][][]){
for(int i=1;i*i<=x;i++){
if(x%i==0&&has[i][x/i]){
System.out.println("Yes");
List<Integer> list1=new ArrayList<>(),list2=new ArrayList<>();
for(int j=i,k=x/i;j>0||k>0;){
int p[]=pre[j][k];
if(p[0]!=j){
list1.add(j-p[0]);
j=p[0];
}
else{
list2.add(k-p[1]);
k=p[1];
}
}
System.out.println(list1.size()+" "+list2.size());
StringBuilder sb1=new StringBuilder(),sb2=new StringBuilder();
for(int a:list1){
sb1.append(a+" ");
}
for(int a:list2){
sb2.append(a+" ");
}
System.out.println(sb1+"\n"+sb2);
return;
}
}
System.out.println("No");
}
}