在题库中随机选题java_TZOJ 挑战题库随机训练09(JAVA)

点击题号跳转

A.传染病控制回到顶部

题意

给一棵树1为根被感染,每次切断一条边,然后从已经被感染的传播到相邻节点,若相连则被传染,问如何操作使得感人的人最少,n<=300

题解

考虑一个贪心做法,显然可以按照深度下来,求出每个点对应的深度,每次切对应深度的点儿子最多的树

显然这样并不能AC,考虑一种情况,一个节点一条链儿子最多,另一个节点分叉,那么如果切分叉,再来切链显然比贪心更优

那么就需要搜索所有的情况,显然按照贪心思路下来,获得的值较优,再来剪枝复杂度会大大降低,复杂度O(常数*n^2)

代码

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 import java.util.*;2

3 public classMain {4

5 privateScanner sc;6

7 public static voidmain(String[] args) {8

9 Scanner sc = newScanner(System.in);10 Solution solver = newSolution();11 solver.solve(sc);12 }13

14 }15

16 classSolution {17

18 private int[][] G = new int[305][305];19 private int[] sz = new int[305];20 private int[] deep = new int[305];21 private int[] father = new int[305];22 private boolean[] vis = new boolean[305];23 private intn, m, deaded;24 public voidsolve(Scanner sc) {25 n =sc.nextInt();26 m =sc.nextInt();27 for (int i = 0; i < m; i++) {28 int u =sc.nextInt();29 int v =sc.nextInt();30 G[u][v] = G[v][u] = 1;31 }32 dfs(1, 1, 1);33 deaded =n;34 dep_dfs(1, 1);35 System.out.println(deaded);36 }37

38 private void dfs(int u, int fa, intdep) {39 sz[u] = 1;40 deep[u] =dep;41 father[u] =fa;42 for (int v = 1; v <= n; v++) {43 if (G[u][v] == 1 && v !=fa) {44 dfs(v, u, dep + 1);45 sz[u] +=sz[v];46 }47 }48 }49

50 private void dep_dfs(int dep, intdead) {51 if (dead >= deaded) return;52 //处理一层节点

53 for (int i = 2; i <= n; i++) {54 if (deep[i] == dep + 1) {55 //System.out.println("i:" + i + " fa:" + father[i]);56 //System.out.println("vis[i]:" + vis[i] + " vis[fa]:" + vis[father[i]]);

57 vis[i] =vis[father[i]];58 }59 }60 //sum代表这一层有多少个会死掉的节点

61 int sum = 0;62 for (int i = 2; i <= n; i++) {63 if (deep[i] == dep + 1 && !vis[i]) {64 sum++;65 }66 }67 //System.out.println("max:" + max);68 //System.out.println("sum:" + sum);69 //这一层所有的节点都活着,结束

70 if (sum == 0) {71 //System.out.println("dead:" + dead);

72 deaded =Math.min(deaded, dead);73 return;74 }75 //选择儿子最多的活下来

76 for (int i = 2; i <= n; i++) {77 if (deep[i] == dep + 1 && !vis[i]) {78 //System.out.println("cut:" + i);

79 vis[i] = true;80 dep_dfs(dep + 1, dead + sum - 1);81 vis[i] = false;82 }83 }84 }85

86 }

A

B.Billing Tables

题意

n个电话,A - B 电话名,[A,B]的电话号表示改电话名,A,B代表的前缀可以合并,12300-12399可以合并为拨打123就可以知道对应电话名

拨一个电话按顺序下来查找对应电话名,其中电话名为invalid表示区间不可用

问这n个电话最少需要多少电话号表示

PS:题意看了n久n久

题解

显然字典树,我们需要把[A,B]区间插进去,很难做到吧

我们可以只插A和B,然后通过A往右,B往左圈定范围,每次把这个范围往下压

倒过来插入,这样可以覆盖掉原先标记的值,还要注意如果电话名相同可以会出现合并,就需要相同电话名标记相同

uL表示A当前的节点,uR表示B

1、如果uL=uR相当于父节点相同,所有子节点为nxt[uL][j]和nxt[uR][j],子节点只需要更新[L[i]+1,R[i]-1]这一段范围

1、为什么不把L[i]和R[i]更新了,因为最后需要遍历整颗树,遍历条件就是当前节点未被标记

2、但是根需要标记L[i]和R[i],这是结束遍历的标志,相当于尾部节点

2、如果uL和uR不相同,相当于uL更新[L[i]+1,9],uR更新[0,R[i]-1]

接下来要输出方案数,如果节点未被标记,往下遍历,如果已被标记,方案数加上1(如果合法)

由于需要求最少的前缀表示,那么就需要合并,可以知道如果某个节点的子节点0-9都合法,那么合并,合并只需要标记u为nxt[u][j],这样在搜索的时候下面就不会搜索而直接返回了

最后输出所有前缀表示,如果节点未被标记,往下遍历,如果已被标记则输出该方案

PS:好艰难意识模糊(看代码的debug就知道快炸了)

代码

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 import java.util.*;2

3 public classMain {4

5 privateScanner sc;6 public static voidmain(String[] args) {7

8 Scanner sc = newScanner(System.in);9 Solution solver = newSolution();10 solver.solve(sc);11 }12

13 }14

15 classSolution {16

17 private final int MAXN = 100005;18 private final int CHAR_SIZE = 10;19

20 private ArrayList L = new ArrayList<>();21 private ArrayList R = new ArrayList<>();22 private ArrayList N = new ArrayList<>();23 private HashMap hashMap = new HashMap<>();24 private int[] pl = new int[105]; //处理相同名字N

25 private boolean[] vis = new boolean[105];//是否非法

26 private int[] mark = new int[MAXN]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值