Round A APAC Test 2017 java完整代码及解答

    xiaoxiao2025-05-28  10

    第一题:country leader

    package googleJam; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner; public class CountryLeader { ArrayList<Character> list = new ArrayList<>(); public CountryLeader(Scanner in) { int T = in.nextInt(); for(int t=1;t<=T;t++){ int result=0; int index=0;//每次循环都要重新赋初值,那么就应该放在循环的开头 int n = in.nextInt(); String[] names = new String[n]; String dele = in.nextLine(); for(int i=0;i<n;i++){ names[i] = in.nextLine();//人名放在数组里 if(differLetter(names[i])>result){ index = i; result = differLetter(names[index]); }else if(differLetter(names[i])==result){ if(names[i].compareTo(names[index])<0){ index = i; result = differLetter(names[index]); } } } System.out.println("Case #"+t+": "+names[index]); } } public static void main(String[] args) throws Exception{ // TODO 自动生成的方法存根 String filename = "A-large-practice.in"; System.setIn(new FileInputStream(filename)); System.setOut(new PrintStream(new FileOutputStream("A-large-practice-out.txt"))); Scanner in = new Scanner(System.in); CountryLeader ex = new CountryLeader(in); in.close(); } public int differLetter(String str){ int count = 0; for(int i=0;i<str.length();i++){ if(!list.contains(str.charAt(i)) && (str.charAt(i)!=' ')){ list.add(str.charAt(i));//使用了26个额外的空间复杂度,但是常数级空间算O(1)还是O(N) count++; } } list.clear(); return count; } } 第二题:rain

    package googleJam; import googleJam.Rain2.Cell; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; import java.util.Comparator; import java.util.PriorityQueue; import java.util.Scanner; //这道题循环遍历不是有网格来决定,而是由优先队列里的队首元素决定,每一个节点都可以先当做边界,不找低地,找最短边界储水 /* * @author Mingfang.z 在别人基础上自己修改的代码 * 思路:先将边界装入优先队列中(高度越小越优先),并标记为已访问。看队首元素四周未访问过的点, * 1、如果该点不比队首低,则将它加入队列,标记为已访问,即它变成了新的边界。 * 2、该点比队首低,意味着该点可以储水,更新res值,同时将它加入队列中,但是它的高度为原队首元素的高度,即以它为边界的点不能超过这个高度, * 同时将该点标记为已访问。 */ public class Rain { /** * @param args */ public Rain(Scanner in){ PriorityQueue<Grid> queue = new PriorityQueue<>(11,new Comparator<Grid>() { public int compare(Grid grid1, Grid grid2){ return grid1.h - grid2.h; } });//接口实现具体方法大括号后面要加分号 int T = in.nextInt(); for(int t=1;t<=T;t++){ int r = in.nextInt(); int c = in.nextInt(); int[][] h = new int[r][c]; for(int i=0;i<r;i++){ for(int j=0;j<c;j++){ h[i][j] = in.nextInt();//输入的原网格 if(i==0||i==r-1||j==0||j==c-1){ queue.add(new Grid(i, j, h[i][j]));//加入外围四条边,队列初始化 } } } boolean[][] visited = new boolean[r][c]; int wCount = 0;//记录储水值 while(!queue.isEmpty()){ Grid grid = queue.poll(); visited[grid.i][grid.j] = true; wCount += waterCount(h, visited, grid.i+1, grid.j, grid.h, queue); wCount += waterCount(h, visited, grid.i-1, grid.j, grid.h, queue); wCount += waterCount(h, visited, grid.i, grid.j-1, grid.h, queue); wCount += waterCount(h, visited, grid.i, grid.j+1, grid.h, queue); } System.out.println("Case #"+t+": "+wCount); } } /*对每一个优先队列里的最小边界节点计算周围储水值 * @param * curH是队首最小边界 * h[i][j]是四周遍历的节点 */ public int waterCount(int[][] h, boolean[][] vis, int i, int j, int curH, PriorityQueue<Grid> queue ){ if(i<0||i>=h.length||j<0||j>=h[0].length||vis[i][j]) return 0; else{ vis[i][j] = true;//判断该节点是否被访问 //curH = queue.poll().h; queue.add(new Grid(i, j, Math.max(curH, h[i][j]))); return Math.max(0, curH-h[i][j]); } } class Grid{//每一个网格的参数需要储存i,j,h int i, j, h; public Grid(int i, int j, int h){ this.i = i;//参数赋值给全局变量,实例变量是对象调用的需要this关键词 this.j = j; this.h = h; } } public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 String filename = "B-small-practice.in"; System.setIn(new FileInputStream(filename)); System.setOut(new PrintStream(new FileOutputStream("B-small-practice-out.txt"))); Scanner in = new Scanner(System.in); new Rain(in); in.close(); System.exit(0); } }

    第三题:jane's flower shop

    花店这道题核心是理解题中给出的公式,找到公式规律。题中给出花店开了M月,和每个月的收入,以及刚开始的总成本,我们可以用一个int c[m+1]来储存,c[0]为总成本cost,其余为相对应每月的收入,这样有点好处就是,c[i]就是第i月的收入,索引不用减一。

    然后写函数方程。很容易找到规律,题中方程为 -c[0]*(1+r)^m+c[1]*(1+r)^(m-1)+````+c[m-1]*(1+r)^1+c[m]*(1+r)^0 = 0;可以看出除了开头的cost部分,每月的收入都是c[i]*(1+r)^(m-i),所以我们在计算函数值得时候,循环变量即为c数组的索引i,从1到m。注意,由于r的精度很高,所以此时必须使用大数计算BigDecimal函数,就是说每一次加减乘除都要先new BigDecimal对象,再用它的实例方法add() multiply(),最后r的结果应该是double型,所以在查找r取值的时候,要以double型。

    代码中的compute函数就是计算当IRR = r的时候根据方程所得函数值,返回的是一个bigdecimal的对象,其实我觉得也可以返回double或int型,提前判断是大于0还是小于0,单我看别人是用bigdecimal.compare去比较,所以也这么写了,可能精确一点吧,毕竟前面都是用它来计算。

    然后是本题的核心部分,二分查找。拿到函数和自变量区间,我们首先应该去考虑它的特性(单调性,最值等),因为本题只有唯一解,所以图像与x轴只有一个交点,且F(-1)>0 因为方程末尾有常数项,所以得出函数单调递减。这样我们再二分查找的时候,当取得r值带进去算的函数值大于0,我们就知道要向右边移动,所以left =middle+0.000000000001,否则就是high = middle-0.000000000001。因为在二分查找的原型中,是用while(l<=r)来控制循环的,所以我们这里也可以借鉴过来,但是要想到,此时的left和right都是精确到小数点后12位的double型(根据题中给的测试用例可以得到),所以无法精确地比较两者大小,所以我们只需要控制他们的精度到小数点后12位就行了,其他的不用管,所以就是while(high-low>=0.000000000001)而当high和low在12位处相等时,我们选择显示两者中的任何一个当做r都可。

    package googleJam; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.math.BigDecimal; import java.util.Scanner; //此题不仅求解方程,对结果的精确度要求高,大数运算,因为small数据只是一次和二次方程,易求,可以先算出来 //java的输出数据格式要规定,format,数据输出精度只能大不能减,double>float public class JaneFlowerShop { /** * @param args */ JaneFlowerShop(Scanner in){ while(in.hasNext()){ int T = in.nextInt(); for(int t = 1;t<=T;t++){ int m = in.nextInt(); long[] c = new long[m+1]; for(int i=0;i<=m;i++){ c[i] = in.nextLong(); } System.out.println(String.format("Case #%d: %.12f", t,irr(c,m)));//格式化输出,保留double数据小数点后12位 } } } //计算参数r的函数值 public BigDecimal compute(long[] c,int m,double r){ BigDecimal r1 = new BigDecimal(1+r); BigDecimal res = new BigDecimal(c[0]).multiply(r1.pow(m)).negate();//初值,用的是bigdecimal的pow不是math的pow for(int i=1;i<=m;i++){ res = res.add(new BigDecimal(c[i]).multiply(r1.pow(m-i))); } return res; } //二分查找r在区间(-1,1)中,先用非递归,返回r public double irr(long[] c,int m){ double low = -1; double high = 1; while(high - low>=0.000000000001){//这只是控制了小数点后12位的精度,但如果high,low并不相等,到达小数点后13位且方程值仍不为0的话,就会跳出循环 double middle = (low+high)/2;//>>1只有int型有 BigDecimal value = compute(c,m,middle); if(value.compareTo(new BigDecimal(0.0))==0) return middle;//Double.valueOf(double d)是double的包装类,将基本类型转换为类的对象 else if(value.compareTo(new BigDecimal(0.0))>0){ low = middle + 0.000000000001;//因为在区间里,函数值value是逆序的 }else if(value.compareTo(new BigDecimal(0.0))<0){ high = middle - 0.000000000001; } } return low;//所以为了在控制的精度内返回r值,就当做当high和low相差小于1e-12时,当做两者相等,查找结束, } public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 String filename = "C-small-practice.in"; System.setIn(new FileInputStream(filename)); System.setOut(new PrintStream(new FileOutputStream("C-small-practice-out.txt"))); Scanner in = new Scanner(System.in); new JaneFlowerShop(in); in.close(); } }

    第四题:clash royale

    这道题题意是:部落冲突是一款即时策略的卡牌游戏,现在需要制定一种策略,使得每次小家伙拿出的8张牌的总攻击力是最大的,在不超过自己拥有金币的前提下。

    思路是借鉴别人的,才敢用暴力,当时在写的时候想到如果穷举,要写八个循环,吓怕了,后来看别人才知道其实还是能过的。

    把八张卡牌分两次计算,前四张暴力穷举用list储存每一种组合所消耗的coins和产生的power,后四张同理。有说可以利用排序来删除无效数据,刚开始我想着也用优先队列来排,但是后来还要考虑筛选有效数据,就放弃了,因为毕竟我都已经用八个循环做了,不在乎这点了。

    计算过程中需要加上一个条件,就是当四张牌消耗的coins总和超过了总的金币M,那这种组合就不用add了。

    编写的时候,我们需要找到循环变量,因为这道题解答的是一种组合方案,而决定我每张牌不同组合的是它的level,也就是每张牌升级不同(等级不同),导致最后打别人的总攻击力不同。所以,计算时,我们的循环变量应该是每张牌的level,从L到K

    下面只实现了small,别人的small都很短,我的写的太长,是奔着large去的,惭愧

    代码中用了几个类来包装每张卡牌的coins和power,相比于用二维数组,觉得清晰一点

    package googleJam; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; import java.util.Scanner; //这道题分两次暴力枚举,前四张牌和后四张牌,本来筛选后排序,可以删除掉无效数据,但是不会删,所以没有用queue //large的话,同样枚举,c(12,8)很小不影响,每次从12张中选出8张牌,计算每8张牌的resA power值并取最大(没写) public class ClashRoyale { /** * @param args */ //每一种方案的排序规则:coins从小到大排,当coins相同时,power从大到小排,都相同时无论先后(没有用到) PriorityQueue<Item> queue = new PriorityQueue<>(11,new Comparator<Item>() { public int compare(Item item1, Item item2){ if(item1.coins<item2.coins) return -1; else if(item1.coins>item2.coins) return 1; else{ if(item1.power>item2.power) return -1; else if(item1.power<item2.power) return 1; else return 0; } } });//接口实现具体方法大括号后面要加分号 List<Item> group1 = new ArrayList<>(); List<Item> group2 = new ArrayList<>();//前后四张牌的每种组合 ClashRoyale(Scanner in){ int T = in.nextInt(); for(int t=1;t<=T;t++){ int M = in.nextInt(); int N = in.nextInt(); long resA = 0;//储存最大power,每张牌最大power10^10,所以总的最大是8*10^10,为long型 //int resC = 0;//储存消耗coins List<Cards> list = new ArrayList<Cards>(8);//cards for(int i=0;i<N;i++){ int k = in.nextInt(); int l = in.nextInt(); int[] a = new int[k]; int[] c = new int[k-1]; for(int j=0;j<k;j++){ a[j] = in.nextInt(); } for(int z=0;z<k-1;z++){ c[z] = in.nextInt(); } list.add(new Cards(k, l, a, c)); } //前四张牌的穷举,计算coins和power int coinsi = 0;int coinsj = 0;int coinsm = 0;int coinsn = 0; for(int i=list.get(0).l;i<=list.get(0).k;i++){//用level变量循环 if(i==list.get(0).l) coinsi = 0; else coinsi += list.get(0).c[i-2]; /* for(int ii=0;ii<i-1;ii++){ coinsi += list.get(0).c[ii]; }*/ for(int j=list.get(1).l;j<=list.get(1).k;j++){ if(j==list.get(1).l) coinsj = 0; else coinsj += list.get(1).c[j-2]; for(int m=list.get(2).l;m<=list.get(2).k;m++){ if(m==list.get(2).l) coinsm = 0; else coinsm += list.get(2).c[m-2]; for(int n=list.get(3).l;n<=list.get(3).k;n++){ if(n==list.get(3).l) coinsn = 0; else coinsn += list.get(3).c[n-2]; if(coinsi+coinsj+coinsm+coinsn<=M) //queue.add(new Item(coinsi+coinsj+coinsm+coinsn, list.get(3).a[n-1]+list.get(2).a[m-1]+ // list.get(1).a[j-1]+list.get(0).a[i-1])); group1.add(new Item(coinsi+coinsj+coinsm+coinsn, list.get(3).a[n-1]+list.get(2).a[m-1]+ list.get(1).a[j-1]+list.get(0).a[i-1])); } } } } //后四张牌计算 int coinsi2 = 0;int coinsj2 = 0;int coinsm2 = 0;int coinsn2 = 0; for(int i2=list.get(4).l;i2<=list.get(4).k;i2++){ if(i2==list.get(4).l) coinsi2 = 0; else coinsi2 += list.get(4).c[i2-2]; for(int j2=list.get(5).l;j2<=list.get(5).k;j2++){ if(j2==list.get(5).l) coinsj2 = 0; else coinsj2 += list.get(5).c[j2-2]; for(int m2=list.get(6).l;m2<=list.get(6).k;m2++){ if(m2==list.get(6).l) coinsm2 = 0; else coinsm2 += list.get(6).c[m2-2]; for(int n2=list.get(7).l;n2<=list.get(7).k;n2++){ if(n2==list.get(7).l) coinsn2 = 0; else coinsn2 += list.get(7).c[n2-2]; if(coinsi2+coinsj2+coinsm2+coinsn2<=M) //queue2.add(new Item(coinsi2+coinsj2+coinsm2+coinsn2, list.get(7).a[n2-1]+list.get(6).a[m2-1]+ // list.get(5).a[j2-1]+list.get(4).a[i2-1])); group2.add(new Item(coinsi2+coinsj2+coinsm2+coinsn2, list.get(7).a[n2-1]+list.get(6).a[m2-1]+ list.get(5).a[j2-1]+list.get(4).a[i2-1])); } } } } //更新resA求最大power(暴力枚举,先不排序了) for(int z =0;z<group1.size(); z++){ for(int y=0;y<group2.size(); y++){ Item it1 = group1.get(z); Item it2 = group2.get(y); if(it1.coins+it2.coins<=M){ resA = Math.max(resA, it1.power+it2.power); } } } System.out.println("Case #"+t+": " + resA); group1.clear(); group2.clear(); } } public static void main(String[] args) throws IOException { // TODO 自动生成的方法存根 String filename = "D-small-practice.in"; System.setIn(new FileInputStream(filename)); System.setOut(new PrintStream(new FileOutputStream("D-small-practice-out.txt"))); Scanner in = new Scanner(System.in); new ClashRoyale(in); in.close(); } // i-th cards param class Cards{ int k,l; int[] a = new int[k];// each level power int[] c = new int[k-l];//upgrading each level coins Cards(int k,int l,int[] a,int[] c){ this.k = k; this.l = l; this.a = a; this.c = c; } } class Item{ int coins; int power; Item(int coins,int power){ this.coins = coins; this.power = power; } } }

    剩下的稍后写

    如果有好的解答,欢迎分享

    转载请注明原文地址: https://ju.6miu.com/read-1299339.html
    最新回复(0)