蓝桥杯2015年省赛[第六届]-JavaB组赛题解析

参考蓝桥杯官网给出的赛题和官方给出的代码。
蓝桥杯官方讲解视频:https://www.lanqiao.cn/courses/2737
真题文档:https://www.lanqiao.cn/courses/2786/learning/?id=67104

时间:4小时
总分:150分

不想做全套题的可以考虑4,7,9,10题


题1.三角形面积[填空][3分]

1.题目描述

如图1所示。图中的所有小方格面积都是1。
那么,图中的三角形面积应该是多少呢?
请填写三角形的面积。不要填写任何多余内容或说明性文字。

在这里插入图片描述

2.简要分析

  • 矩形面积减去三个三角形面积。(64-36=28)

3.答案

28


题2.立方变自身[填空][5分]

1.题目描述

观察下面的现象,某个数字的立方,按位累加仍然等于自身。

1^3 = 1
8^3 = 512 5+1+2=8
17^3 = 4913 4+9+1+3=17


请你计算包括1,8,17在内,符合这个性质的正整数一共有多少个?

请填写该数字,不要填写任何多余的内容或说明性的文字。

2.简要分析

  • 题目很简单,不过有点模糊的就是这个正整数的范围。
  • 如果硬做的话,我们循环到9999或者99999,看看最后的个数是否稳定就可以了。
  • 这里我们可以简单的推理一下,假设这个数字是个三位数,那么100^3=1000000,是个七位数,七位数相加,最大可能是7*9=63,已经小于100了,所有三位数就是不可能的了,我们只需要循环到99即可。

3.实现代码(模拟)

public class _02立方变自身 {
  private static int ans;

  public static void main(String[] args) {
    for (int i = 1; i <99; i++) {
      int i1 = i * i * i;
      int sum=sum(i1);
      if (sum==i){
        System.out.println(i+" "+i1);
        ans++;
      }
    }
    System.out.println(ans);
  }

  private static int sum(int x) {
    String s=String.valueOf(x);
    int sum=0;
    for (int i = 0; i < s.length(); i++) {
      sum+=s.charAt(i)-'0';
    }
    return sum;
  }
}//6

4.答案

6


题3.三羊献瑞[填空][9分]

1.题目描述

观察下面的加法算式:

      祥 瑞 生 辉
  +   三 羊 献 瑞
-------------------
   三 羊 生 瑞 气

在这里插入图片描述

其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。

请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。

2.简要分析

  • 初步的看一下这个算式,可以基本确定,就是1(两位数加法进位),那么就是9就是0,后面似乎无法确定下来,不过也可以得到一些大小关系,无妨用字母替代:
      a b c d
  +   e f g b
-------------------
    e f c b i

   e=1,a=9,f=0,c=b+1,c+g>10
  • 我们可以枚举剩下的数字b,d,g,i。这四个数字不相等,且不可能是已经确定了的0,1,9

3.实现代码(枚举)

public class _03三羊献瑞 {
  public static void main(String[] args) {
    for (int b = 2; b < 9; ++b) {
      for (int d = 2; d < 9; ++d) {
        if (b == d) continue;
        for (int g = 2; g < 9; ++g) {
          if (g == b || g == d) continue;
          int c = b + 1;
          if (c == b || c == d || c == g) continue;
          if (c + g <= 10) continue;
/*
      a b c d
  +   e f g b
-------------------
    e f c b i
   e=1,a=9,f=0,c=b+1,c+g>10
   */
          int sum = 9000 + b * 100 + c * 10 + d + 1000 + g * 10 + b;
          for (int i = 2; i < 9; ++i) {
            if (i == b || i == d || i == g || i == c) continue;
            if (sum == (10000 + c * 100 + b * 10 + i)) {
              System.out.printf("%2d%d%d%d\n", 9, b, c, d);
              System.out.printf("%2d%d%d%d\n", 1, 0, g, b);
              System.out.printf("---------\n");
              System.out.printf("%d\n", sum);
            }
          }

        }
      }
    }

  }
}

4.答案

1085


题4.循环节长度[代码填空][11分]

1.题目描述

两个整数做除法,有时会产生循环小数,其循环部分称为:循环节。
比如,11/13=6=>0.846153846153… 其循环节为[846153] 共有6位。
下面的方法,可以求出循环节的长度。

请仔细阅读代码,并填写划线部分缺少的代码。

	public static int f(int n, int m)
	{
		n = n % m;//11
		Vector v = new Vector();

		for(;;)
		{
			v.add(n);//11 6
			n *= 10;//110 60
			n = n % m;//6
			if(n==0) return 0;
			if(v.indexOf(n)>=0)  _________________________________ ;  //填空
		}
	}

注意,只能填写缺少的部分,不要重复抄写已有代码。不要填写任何多余的文字。

2.简要分析

  • 分析这段代码,先以一种大概猜测的形式去做,经过一些运算,将一些数字加入向量中,然后如果发现n已经在v里面有了,应该执行一个什么样的操作。大胆猜测,肯定就是这个时候找到了循环节,那么这个向量里面装的应该就是小数部分,循环节的长度应该等于此时v的长度减去第一次出现n的位置(下标)。

  • 当然上面只是我们的一种猜测,接下来仔细分析一下代码。用11和13模拟一下。

    • 首先n=11%13=11。
    • 然后创建了一个vector。接下来一直循环。
    • 将n放入v,此时的v:11
    • n=110。
    • n=110%13=6。
    • 将n放入v,此时v:11,6
    • n=60。
    • n=60%13=8。
    • 将n放入v,此时v:11,6,8
    • ..........
  • 通过上面的模拟我们可以发现,题目中的代码实际上是在模拟除的过程,不管它关注的只是余数,v里面存的也是余数,这样我们就可以发现,如果在计算过程中遇到两个一样的于是,因为m是固定的,按照问我们常规的除法计算,这里肯定是存在循环的,中间每一个余数对应的是一位小数,所以循环节的长度就是v的长度减去n第一次在v中出现的位置(下标)。

  • 我们第一次的大胆猜测,虽然答案没有错,但其实原理是不对的,哈哈哈。

3.实现代码

public class _04_循环节长度 {
  public static int f(int n, int m) {
    n = n % m;
    Vector v = new Vector();

    for (; ; ) {
      v.add(n);
      n *= 10;
      n = n % m;
      if (n == 0) return 0;
      if (v.indexOf(n) >= 0)
        return v.size() - v.indexOf(n);  //填空
    }
  }
  
  public static void main(String[] args) {
    System.out.println(f(11, 13));
    System.out.println(f(7, 18));
    System.out.println(11%13);
  }
}

4.答案

return v.size() - v.indexOf(n)


题5.九数组分数[代码填空][15分]

1.题目描述

1,2,3…9 这九个数字组成一个分数,其值恰好为1/3,如何组法?

下面的程序实现了该功能,请填写划线部分缺失的代码。

public class A
{
  public static void test(int[] x)
  {
    int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
    int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
    if(a*3==b) System.out.println(a + " " + b);
  }

  public static void f(int[] x, int k)
  {
    if(k>=x.length){
      test(x);
      return;
    }

    for(int i=k; i<x.length; i++){
      {int t=x[k]; x[k]=x[i]; x[i]=t;}
      f(x,k+1);
      _______________________________________       // 填空
    }
  }

  public static void main(String[] args)
  {
    int[] x = {1,2,3,4,5,6,7,8,9};
    f(x,0);
  }
}

注意,只能填写缺少的部分,不要重复抄写已有代码。不要填写任何多余的文字。

2.简要分析

  • 读完题,发现是一个全排列问题,再看代码,果然是,看空缺的地方,其实就是全排列中恢复两个交换值的地方。
  • 需要特别注意的是,上面的交换代码用括号括起来了,里面声明的变量t不能在下面直接使用!

3.实现代码

public class _05九数组分数 {
  public static void test(int[] x)
  {
    int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
    int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
    if(a*3==b) System.out.println(a + " " + b);
  }

  public static void f(int[] x, int k)
  {
    if(k>=x.length){
      test(x);
      return;
    }

    for(int i=k; i<x.length; i++){
      {int t=x[k]; x[k]=x[i]; x[i]=t;}
      f(x,k+1);
      {int t=x[k]; x[k]=x[i]; x[i]=t;}      // 填空
    }
  }

  public static void main(String[] args)
  {
    int[] x = {1,2,3,4,5,6,7,8,9};
    f(x,0);
  }
}

4.答案

{int t=x[k]; x[k]=x[i]; x[i]=t;}


题6.加法变乘法[填空][17分]

1.题目描述

我们都知道:1+2+3+ … + 49 = 1225 现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015

比如: 1+2+3+…+1011+12+…+2728+29+…+49 = 2015 就是符合要求的答案。

请你寻找另外一个可能的答案,并把位置靠前的那个乘号左边的数字提交(对于示例,就是提交10)。

注意:需要你提交的是一个整数,不要填写任何多余的内容。

2.简要分析

  • 双层循环暴力枚举两个乘号的位置即可。

3.实现代码(暴力枚举)

public class _06_加法变乘法 {
  public static void main(String[] args) {
    for (int i = 1; i <= 46; i++) {
      for (int j = i + 2; j <= 48; j++) {
        if (i * (i + 1) - (i + i + 1) + j * (j + 1) - (j + j + 1) == 2015 - 1225)
          System.out.println(i + " " + j);
      }
    }
  }
}

4.答案

16


题7.牌型种数[填空][21分]

1.题目描述

小明被劫持到X赌城,被迫与其他3人玩牌。
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?

请填写该整数,不要填写任何多余的内容或说明文字。

2.简要分析

3.实现代码(无顺序全排列)

public class _07_牌型种数 {
  private static int ans;

  public static void main(String[] args) {
    f(0, 0);
    System.out.println(ans);
  }

  private static void f(int k, int cnt) {
    if (k > 13 || cnt > 13) return;
    if (k == 13 && cnt == 13) {
      ans++;
      return;
    }
    for (int i = 0; i < 5; i++) {
      f(k + 1, cnt + i);
    }
  }
}

4.答案

3598180


题8.饮料换购[13分]

1.题目描述

乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去,但不允许赊账。

请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的n瓶饮料,最后他一共能得到多少瓶饮料。

输入:一个整数n,表示开始购买的饮料数量(0<n<10000)
输出:一个整数,表示实际得到的饮料数
例如:
用户输入:
100
程序应该输出:
149
用户输入:
101
程序应该输出:
151

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

2.简要分析

  • 简单模拟一下换饮料的过程即可。
  • 初始总共得到的饮料数为n。
  • 当n>=3时,每换一次饮料相当于n-=2,总共得到的饮料数+1。

3.实现代码(简单模拟)

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int ans = n;
    while (n >= 3) {
      n -= 2;
      ans += 1;
    }
    System.out.println(ans);
  }
}

题9.垒骰子[25分]

1.题目描述

赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。 atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模 10^9 + 7 的结果。

不要小看了 atm 的骰子数量哦~

「输入格式」
第一行两个整数 n m
n表示骰子数目
接下来 m 行,每行两个整数 a b ,表示 a 和 b 不能紧贴在一起。
「输出格式」
一行一个数,表示答案模 10^9 + 7 的结果。
「样例输入」
2 1
1 2
「样例输出」
544
「数据范围」
对于 30% 的数据:n <= 5
对于 60% 的数据:n <= 100
对于 100% 的数据:0 < n <= 10^9, m <= 36

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

2.简要分析

  • 题目很好理解,就是n个骰子,在满足对应的面不贴在一起的时候,总共有多少种可能性。大概方法分析一下:

    • 首先,很好理解的就是,骰子的边上四个面都可以旋转,所以在垒好n个骰子之后,因为旋转侧面,总的可能数会是4的n次方,这样只要计算不需要考虑旋转情况下垒好n个骰子的可能数m,最后的结果就是4的m次方。不过由于这个数或很大,所以最好使用快速幂的方法去计算这个结果。
    • 我们可以拿例子来简单的看一下,两个骰子不考虑四边旋转的时候,可能数是6*6-2=34(有两个会冲突),然后再乘上2的四次方16,最后的结果就是544.
    • 对于每一个骰子,都有6个面,每一个面都可以做为一个朝上的面与其他面接触(需要排除产生冲突的情况)。所以,用递归或者迭代的方式,都是可以做出来的。但是数据的规模达到了10的9次方,这两种方法肯定都会超时。
    • 这题的标准思路是矩阵的方法。
  • 下面是三种实现方式,只有矩阵快速幂的方式是可以完全通过测试案例的。

3.实现代码1(递归)(会超时)

  • 递归实现的方式很好理解,从第n个骰子开始,向下递归,如果冲突就不计算,出口是cnt=0,也就是递归到第一个骰子,此时返回1.
  • 但是多达10^9的递归,毫无疑问会超时。
  • 递归方面,也没得好的优化办法。
public class _2015_b9_1 {

    static int op[] = new int[7];
    static long conflict[][] = new long[7][7];
    private static int n;
    private static int m;
    private static final long MOD = 1000000007;

    //初始化骰子的对立面
    static void init() {
        op[1] = 4;
        op[4] = 1;
        op[2] = 5;
        op[5] = 2;
        op[3] = 6;
        op[6] = 3;
    }

    public static void main(String[] args) {
        init();
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();

        //初始化冲突矩阵 - 1代表没有冲突
        for (int i = 0; i <=6; i++) {
            for (int j = 0; j<=6; j++) {
                conflict[i][j]=1;
            }
        }
        //建立冲突矩阵
        for (int i = 0; i < m; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            //0代表有冲突的面
            conflict[op[a]][b] = 0;
            conflict[op[b]][a] = 0;
        }
        long ans=0;
        for(int i=1;i<=6;i++){
            ans=(ans+f(i,n-1))%MOD;
        }
        System.out.println(ans * power(4, n) % MOD);
    }

    private static long f(int up,int cnt){
        if(cnt==0){
            return 1;
        }
        long ans=0;
        for(int i=1;i<=6;i++){
           if(conflict[op[up]][i]==0) continue;
           ans+=f(i,cnt-1);
        }
        return ans;
    }

    //快速幂
    private static long power(long i, int n) {
        long ans = 1;
        while (n != 0) {
            if ((n & 1) == 1) ans = (ans * i) % MOD;
            i = i * i % MOD;
            n >>= 1;
        }
        return ans;
    }
}

4.实现代码2(动态规划)(会超时)

  • dp的方式需要能找出合适的递推关系。
  • 通过递归的思路,我们可以得出如下的递推关系式:
  • 假设dp[i][j]表示第i层,限定朝上的数字为j的方案数。
  • 那么我们可以得到递推关系式子:dp[i][j]=(求和x从1到6)dp[i-1][x] | x与op[j]不冲突
  • 因为每一层都只和上一层有关系,所以dp的第一维的长度可以缩小,能够减少大量空间,只需dp[2][7]即可。
  • 这个dp还是比较好理解的。
  • 但是时间复杂度达到O(36n),还是会超时。
public class _2015_b9_2 {

    static int op[] = new int[7];
    static long conflict[][] = new long[7][7];
    static long dp[][]=new long [2][7];
    private static int n;
    private static int m;
    private static final long MOD = 1000000007;

    //初始化骰子的对立面
    static void init() {
        op[1] = 4;
        op[4] = 1;
        op[2] = 5;
        op[5] = 2;
        op[3] = 6;
        op[6] = 3;
    }

    public static void main(String[] args) {
        init();
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();

        //初始化冲突矩阵 - 1代表没有冲突
        for (int i = 0; i <=6; i++) {
            for (int j = 0; j<=6; j++) {
                conflict[i][j]=1;
            }
        }
        //建立冲突矩阵
        for (int i = 0; i < m; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            //0代表有冲突的面
            conflict[op[a]][b] = 0;
            conflict[op[b]][a] = 0;
        }

        for(int i=1;i<=6;i++){
            dp[0][i]=1;

        }
        int cur=0;
        //最外层循环-迭代的层数
        for(int floor=2;floor<=n;floor++){
            //滚动数组
            cur=1-cur;
            //尝试将六面作为朝上面
            for(int j=1;j<=6;j++){
                dp[cur][j]=0;
                //累加不冲突的面
                for(int i=1;i<=6;i++){
                    //有冲突
                    if(conflict[op[j]][i]==0) continue;
                    dp[cur][j]=(dp[cur][j]+dp[1-cur][i])%MOD;
                }
            }
        }

        long ans=0;
        for(int k=1;k<=6;k++){
            ans=(ans+dp[cur][k])%MOD;
        }
        System.out.println(ans * power(4, n) % MOD);
    }


    //快速幂
    private static long power(long i, int n) {
        long ans = 1;
        while (n != 0) {
            if ((n & 1) == 1) ans = (ans * i) % MOD;
            i = i * i % MOD;
            n >>= 1;
        }
        return ans;
    }
}

5.实现代码3(矩阵快速幂)

  • 用矩阵实现的关键在于要能发现:(冲突矩阵的n-1次方)*(6行全1列向量)=第一个骰子每一个面可能种数构成的矩阵。
  • 具体如何发现的,可以根据dp关系推导递推矩阵,也可以从头开始尝试。
  • 矩阵的n-1次方可以使用矩阵快速幂的方式实现,这样时间复杂度就可以降下来了。
public class Main {
  static int op[] = new int[7];
  private static int n;
  private static int m;
  private static final long MOD = 1000000007;

  //初始化骰子的对立面
  static void init() {
    op[1] = 4;
    op[4] = 1;
    op[2] = 5;
    op[5] = 2;
    op[3] = 6;
    op[6] = 3;
  }

  public static void main(String[] args) {
    init();
    Scanner sc = new Scanner(System.in);
    n = sc.nextInt();
    m = sc.nextInt();
    long conflict[][] = new long[6][6];
    //初始化冲突矩阵 - 1代表没有冲突
    for (int i = 0; i < 6; i++) {
      for (int j = 0; j < 6; j++) {
        conflict[i][j]=1;
      }
    }
    //建立冲突矩阵
    for (int i = 0; i < m; i++) {
      int a = sc.nextInt();
      int b = sc.nextInt();
      //0代表有冲突的面
      conflict[op[a] - 1][b - 1] = 0;
      conflict[op[b] - 1][a - 1] = 0;
    }
    //  求冲突矩阵的n-1次方
    long[][] mPow_n_1 = mPow(conflict, n - 1);
    //累加矩阵的每个元素
    long ans = 0;
    //枚举第一个骰子和第二个骰子面相邻的所有可能
    for (int i = 0; i < 6; i++) {
      for (int j = 0; j < 6; j++) {
        ans = (ans + mPow_n_1[i][j]) % MOD;
      }
    }
    //ans*4^n
    System.out.println(ans * power(4, n) % MOD);
  }

  //快速幂
  private static long power(long i, int n) {
    long ans = 1;
    while (n != 0) {
      if ((n & 1) == 1) ans = (ans * i) % MOD;
      i = i * i % MOD;
      n >>= 1;
    }
    return ans;
  }

  /*矩阵的快速幂*/
  private static long[][] mPow(long[][] conflict, int n) {
    long[][] e = new long[6][6];
    for (int i = 0; i < 6; i++) {
      for (int j = 0; j < 6; j++) {
        if (i == j) e[i][j] = 1;
        else e[i][j] = 0;
      }
    }
    while (n != 0) {
      if ((n & 1) == 1) {
        e = mMul(e, conflict);
      }
      conflict = mMul(conflict, conflict);
      n >>= 1;
    }

    return e;
  }

  //矩阵乘法
  private static long[][] mMul(long[][] a, long[][] b) {
    long[][] ans = new long[6][6];
    for (int i = 0; i < 6; i++) {
      for (int j = 0; j < 6; j++) {
        for (int k = 0; k < 6; k++) {
          ans[i][j] = (ans[i][j] + a[i][k] * b[k][j]) % MOD;
        }
      }
    }
    return ans;
  }
}

题10.生命之树[31分]

1.题目描述

在X森林里,上帝创建了生命之树。

他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。
上帝要在这棵树内选出一个非空节点集S,使得对于S中的任意两个点a,b,都存在一个点列 {a, v1, v2, …, vk, b} 使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。

在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。

这个最大的和就是上帝给生命之树的评分。

经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。但是由于 atm 不擅长计算,他不知道怎样有效的求评分。他需要你为他写一个程序来计算一棵树的分数。

「输入格式」
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。由于这是一棵树,所以是不存在环的。
「输出格式」
输出一行一个数,表示上帝给这棵树的分数。
「样例输入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
「样例输出」
8
「数据范围」
对于 30% 的数据,n <= 10
对于 100% 的数据,0 < n <= 10^5, 每个节点的评分的绝对值不超过 10^6 。

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 3000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

2.简要分析

  • 由题意可以知道,这是一棵无根树。
  • 对于无根树,我们可以将每一个节点做为根节点去计算最大的值,最后取所有可能里面的最大值。
  • 具体实现见代码。

3.实现代码

public class Main {

  private static int n;
  //存储所有节点的权值
  private static long[] w;
  //
  private static List<Integer>[] g;
  private static long ans ;

  public static void main(String[] args) throws FileNotFoundException {
    Scanner sc = new Scanner(System.in);
    n = sc.nextInt();
    w = new long[n + 1];
    g = new ArrayList[n + 1];
    initG();
    for (int i = 1; i <= n; i++) {
      w[i] = sc.nextLong();
    }
    for (int i = 0; i < n - 1; i++) {
      int a = sc.nextInt();
      int b = sc.nextInt();
      g[a].add(b);
      g[b].add(a);
    }
    dfs(1, 0);
    System.out.println(ans);
  }

  /**
   * root做为根所代表的子树有一个最大权和,将其存储在w[root]中
   * @param root
   * @param fa
   */
  private static void dfs(int root, int fa) {
    //遍历每一个相连的节点
    for (int i = 0; i < g[root].size(); i++) {
      Integer child = g[root].get(i);
      if (child == fa) continue;
      //递归计算
      dfs(child, root);
      if (w[child] > 0)
        w[root] += w[child];
    }
    //不断更新最大值
    if (w[root] > ans) ans = w[root];
  }
  //初始化二维集合g
  private static void initG() {
    for (int i = 0; i < n + 1; i++) {
      g[i] = new ArrayList<Integer>();
    }
  }
}

总结

  • 做一遍下来头晕了~~~

  • 1-8题难度不大,最后两题还是有一定难度的。

  • 梳理一下考点:

    • 1.暴力枚举。
    • 2.代码分析。
    • 3.无序的全排列。
    • 4.经典全排列。
    • 5.过程模拟。
    • 6.矩阵快速幂。
    • 7.无根树的转换。
  • 矩阵快速幂和无根树的转换是这套题目的重点了。


ATFWUS Writing 2021-1-23

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ATFWUS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值