最近想要参加今年的CodeM竞赛,所以把去年的题做了一遍,顺便写个题解,做个记录。

资格赛有A-F,共6道题,资格赛不限时,而且只要完成一道题就可以了。

A、数码

题意:给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。

示例1

输入

1 4

输出

4
2
1
1
0
0
0
0
0

这道题只要照着题目意思写代码,就可以通过50%的样例。但是,因为时间复杂度太大,并不能通过所有样例。

解题思路:

这道题一个很关键的点是,在[1,x]中,能够整除y的数有x/y个。

所以,将求[left,right]的问题,拆成求[1,right]-[1,left-1]的问题就变得简单很多。

下面是计算[1,x]的代码:

    private static void compute(long[] a, int x) {for (int i = 1; i <= 9; i++) {   // i为约数y的最高位数字for (long j = 1; i * j <= x; j *= 10) { //构造以i为最高位数字的约数//约数y 从 [i*j,min((i+1)*j-1,x)],[1,1],[10,19],[100,199]...// 例如:[10,19](i=1,j=10,x>19)long start = i * j;long end = Math.min((i + 1) * j - 1, x);long slip;for (long y = start; y <= end; y += slip) {long mul = x / y;  //[1,x]中能够整除y的个数为x/ylong remain = x - mul * y; //大于y却不能整除y的有remain个//slip为在[start,end]区间中与y值具有相同mul的值的个数slip = 1 + Math.min(remain / mul, end - y);a[i] += mul * slip;}}}}

B、音乐研究

题意:在第二个数组中找到一个长度和第一个数组相等且是连续的子序列,使得它们的 difference 最小。两个等长数组的 difference 定义为:difference = SUM(a[i] - b[i])2 (1 ≤ i ≤ n)

示例1

输入

2
1 2
4
3 1 2 4

输出

0

这道题比较简单,用两个for循环就可以解决。

int min = Integer.MAX_VALUE;for (int i = 0; i < len2 - len1; i++) {int sum = 0;int k = i;for (int j = 0; j < len1; j++) {sum += (a[j] - b[k]) * (a[j] - b[k]);k++;}if (min > sum) {min = sum;}}System.out.println(min);

C、锦标赛

题意:比赛有 n 个人参加(其中 n 为2的幂),每个参赛者根据资格赛和预赛、复赛的成绩,会有不同的积分。比赛采取锦标赛赛制,分轮次进行,设某一轮有m 个人参加,那么参赛者会被分为 m/2 组,每组恰好 2 人,m/2 组的人分别厮杀。我们假定积分高的人肯定获胜,若积分一样,则随机产生获胜者。获胜者获得参加下一轮的资格,输的人被淘汰。重复这个过程,直至决出冠军。现在请问,参赛者小美最多可以活到第几轮(初始为第0轮,小美是第一个参赛者)?

示例1

输入

4
4 1 2 3

输出

2

先求比小美积分少或者积分与小美一样的人数(这里设为n-1),小美最多能在这部分人中到达最后一轮,log2(n)是小美最多能够活到的轮数。在java中求log2(n),需要求log(n)/log(2)。

  int n = sc.nextInt();int[] score = new int[n];score[0] = sc.nextInt();int tag = score[0];int greater = 1;for (int i = 1; i < n; i++) {score[i] = sc.nextInt();if (score[i] <= tag) {greater += 1;}}//int after=(int)(Math.log(greater)/Math.log(2));int after = 0;while (greater > 1) {greater /= 2;after += 1;}System.out.println(after);

D、送外卖

题意:给出n个点,从0到n-1编号,从i号点出发按照方案a可以到达i+ai号点,按照方案b可以到达i+bi号点,不允许走出这n个点,你需要判断是否存在从0号点出发到达n-1号点的方案,如果不存在输出“No solution!”,否则输出字典序最小的方案,如果字典序最小的方案长度无限,则输出“Infinity!”。

示例1

输入

7
5 -3 6 5 -5 -1 6
-6 1 4 -2 0 -2 0

输出

abbbb

这是一道我连题目都看不懂的题,下面解释一下例子的意思。

初始时从0号出发,选择a0,则到达0+a0=5号点,在5号选择b5,则到达5+b5=5-2=3号点,在3号点选择b3,到达3-2=1号点,在1号点选择b1,到达1+1=2号点,在2号点选择b2,到达2+b2=6,即到达n-1号点,所以,输出的最小字典序方案为abbbb。

解题思路:首先使用一个数组G将所有可以到达t(t = 0~n-1)点的点都存起来,然后使用bfs从后向前遍历G,求得一个vis数组,若vis[0]被访问过,则说明存在一条从0到n-1的路径,否则无解。从起点0开始根据vis求字典序最小的路径,先查看a方案是否是可以到达n-1点的方案,若是,再查看a方案到达的点是否被再次访问过,若是则说明路径中存在环路,则输出“Infinity!”。同理,对方案b也一样。

下面是代码:

 public static void main(String[] args) {Scanner sc = new Scanner(System.in);while (sc.hasNext()) {int n = sc.nextInt();int[] a = new int[n];int[] b = new int[n];String[] G = new String[n];String str = "";int[] vis = new int[n];int[] vis2 = new int[n];for (int i = 0; i < n; i++)G[i] = "\t";for (int i = 0; i < n; i++) {a[i] = sc.nextInt();int t = i + a[i];if (t >= 0 && t < n) {//G存储可以到达t点的所有点(t = 0~n-1)G[t] += i + "\t";}}for (int i = 0; i < n; i++) {b[i] = sc.nextInt();int t = i + b[i];if (t >= 0 && t < n) {G[t] += i + "\t";}}//System.out.println(Arrays.toString(G));//从后向前使用广度优先搜索,求搜索路径rev_bfs(n - 1, G, vis);if (vis[0] == 0) {//从后向前搜索,未能到达初始点0System.out.println("No solution!");} else {//能够到达初始点0,则求最小字典序方案int p = 0;vis2[0] = 1; //前向访问数组boolean infflag = false;for (int x = 0; x != n - 1 && !infflag; ) {int nxt = x + a[x];if (nxt >= 0 && nxt < n && vis[nxt] == 1) {if (vis2[nxt] == 0) {vis2[nxt] = 1;str += 'a';} else {//若已访问过,说明存在环,使得方案无限infflag = true;}x = nxt;} else {nxt = x + b[x];if (nxt >= 0 && nxt < n && vis[nxt] == 1) {if (vis2[nxt] == 0) {vis2[nxt] = 1;str += 'b';} else {infflag = true;}} else {System.out.println("No solution!");}x = nxt;}}System.out.println(infflag ? "Infinity!" : str);}}}private static void rev_bfs(int p, String[] G, int[] vis) {Queue<Integer> q = new ArrayDeque<>();vis[p] = 1;q.add(p);while (!q.isEmpty()) {//System.out.println(q.toString());int x = q.poll();String[] str = G[x].trim().split("\t");for (int i = 0; i < str.length; i++) {int t;try {t = Integer.valueOf(str[i]);} catch (Exception e) {continue;}if (vis[t] == 0) {vis[t] = 1;q.add(t);}}}}

E、围棋

题意:给出围棋的规则,输入一些操作,从空棋盘开始模拟这些操作。对于每一步,若结果不正确,则输出对应的miss并忽略这个操作,最后输出棋盘的局面。

解题思路:根据题意模拟。这里的三个错误:miss 1 是重复放子,比较好盘判断,只要棋盘上相应位置已经存在棋子,再想要将棋子放到那里,就是miss 1。miss 2 是将棋子放在禁放位上,即,将棋子放入后,周围环绕着它的都是与它不同色的棋子,除非,将棋子放入后可以将它周围一个异色棋子给拿出来(因为这个异色棋子周围环绕的都是和它异色的棋子),用DFS检查这个棋子周围是否都是异色棋子。miss 3 是判断当前棋盘局面是否和历史棋盘局面一样,可以利用hash判断当前棋盘局面是否出现过。

package CodeM;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;class hascode {long has0;long has1;public hascode(long has0, long has1) {this.has0 = has0;this.has1 = has1;}
}public class GameGo_2017 {static int[][] dir = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};static int goNum = 19;public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();while (n-- > 0) {int Num = sc.nextInt();char[][] go = new char[goNum][goNum];for (int i = 0; i < go.length; i++) {// inital go boardfor (int j = 0; j < go.length; j++)go[i][j] = '.';}//use st to store the hashcode of go board,// to compare the next go board and the past go board.ArrayList<Long> st = new ArrayList<>();check(go, st);while (Num-- > 0) {String op = sc.next();int x = sc.nextInt();int y = sc.nextInt();x -= 1;y -= 1;//System.out.println("first : "+op);char a = op.charAt(0);char b = a == 'B' ? 'W' : 'B';if (go[x][y] != '.') {//exist one point.System.out.println("miss 1");} else {//use nextgo to get the next step//if this step can be done then cur equals to nextgo//otherwise print the mistake.char[][] nextgo = new char[goNum][goNum];copy(nextgo, go);/*System.out.println("==============================");for (int i = 0; i < goNum; i++) {System.out.println(Arrays.toString(nextgo[i]));}*/nextgo[x][y] = a;//use vis[][] to distinguish one poist is visited or not.int[][] vis = new int[goNum][goNum];//check this step is conformed to the 2nd or not//that is whether exist a or not,if not that is the 2nd mistake.boolean isOk = dfs(x, y, a, vis, nextgo);for (int i = 0; i < dir.length; i++) {int tx = x + dir[i][0];int ty = y + dir[i][1];//!dfs(tx,ty,b,vis,nextgo) means there are all a around [tx,ty]//according the go rules,must pick the go point.if (tx >= 0 && tx < goNum && ty >= 0 && ty < goNum &&vis[tx][ty] == 0 && nextgo[tx][ty] == b&& !dfs(tx, ty, b, vis, nextgo)) {pick(tx, ty, b, nextgo);isOk = true;}}if (isOk) {// if this step will not appear the first two mistakes// now check if the same as the the past one go board.// use hashcode to compare.if (check(nextgo, st)) {//if this step is pass the check,then the cur is// the same as the nextcopy(go, nextgo);} else System.out.println("miss 3");} else System.out.println("miss 2" );}}//show();for (int i = 0; i < goNum; i++) {for (int j = 0; j < goNum; j++) {System.out.print(go[i][j]);}System.out.println();}}}private static boolean dfs(int x, int y, char op, int[][] vis, char[][] next) {//check whether this point is surrounded by different pointsif (next[x][y] == '.') return true;if (next[x][y] != op) return false;vis[x][y] = 1;boolean isOk = false;for (int i = 0; i < dir.length; i++) {int tx = x + dir[i][0];int ty = y + dir[i][1];if (tx >= 0 && tx < goNum && ty >= 0 && ty < goNum && vis[tx][ty] == 0) {isOk |= dfs(tx, ty, op, vis, next);}}return isOk;}private static void pick(int x, int y, char op, char[][] next) {next[x][y] = '.';for (int i = 0; i < dir.length; i++) {int tx = x + dir[i][0];int ty = y + dir[i][1];if (tx >= 0 && tx < goNum && ty >= 0 && ty < goNum && next[tx][ty] == op)pick(tx, ty, op, next);}}private static boolean check1(char[][] next, ArrayList<hascode> st) {long has1 = 0, has2 = 0;for (int i = 0; i < goNum; i++) {for (int j = 0; j < goNum; j++) {has1 = has1 * 131 + next[i][j];has2 = has2 * 137 + next[i][j];}}for (hascode has : st) {if (has1 == has.has0 && has2 == has.has1)return false;}hascode hasCode = new hascode(has1, has2);st.add(hasCode);return true;}private static boolean check(char[][] next, ArrayList<Long> st) {long has1 = 0;for (int i = 0; i < goNum; i++) {for (int j = 0; j < goNum; j++) {has1 = has1 * 131 + next[i][j];//has2 = has2 * 137 + next[i][j];}}if (st.contains(has1)){return false;}//hascode hasCode = new hascode(has1, has2);st.add(has1);return true;}private static void copy(char[][] next, char[][] go) {for (int i = 0; i < goNum; i++)for (int j = 0; j < goNum; j++)next[i][j] = go[i][j];}}

原本是利用两个hash值存储棋盘局面的,但是,时间消耗太大,为了减少时间复杂度,改成了只使用一个hash值,终于通过了测试,原本的代码也没有删掉,所有都保留了,供以后参考。

F、优惠券

题意:按照时间顺序给出m条记录,每条记录可能是购买了x号优惠券,可能是使用了x号优惠券,也可能是未知,要求出最大的s使得第1到第s-1条记录是正确的,如果所有记录都正确那么输出-1。

这道题除了题目的提到的规则外,还有一个隐形规则:在优惠券没有使用的情况下,不允许重复购买相同编号的优惠券。这意味着在某编号优惠券数量大于1时,需要在这两个操作之间找到一个未知操作,作为优惠券的使用操作,使拥有的优惠券数量都不大于1.

package CodeM;import java.util.ArrayList;
import java.util.Scanner;public class coupon_2017ans {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int maxN = 100005;int m = sc.nextInt();int res = -1;ArrayList<Integer> mark = new ArrayList<>();int[] cnt = new int[maxN];int[] la = new int[maxN];for (int i = 1; i <= m; i++) {String op = sc.next();if (op.equals("?")) {mark.add(i);continue;}int x = sc.nextInt();cnt[x] += (op.equals("I") ? 1 : -1);if (cnt[x] < 0 || cnt[x] > 1) {int t = find(la[x], mark);if (t == -1) {res = i;break;}mark.remove(t);cnt[x] = cnt[x] < 0 ? 0 : 1;//cnt[x]=Math.min(Math.max(cnt[x],0),1);}la[x] = i;}System.out.println(res);}private static int find(int la, ArrayList<Integer> mark) {if (mark.size() == 0) return -1;int low = 0, high = mark.size() - 1;while (low < high) {int mid = (low + high) / 2;int t = mark.get(mid);if (t <= la) {low = mid + 1;} else {high = mid;}}//int res=low==mark.size()?-1:low;int res;if (mark.get(low) > la) {res = low;} else res = -1;return res;}
}

总结:

花了好几天终于把这篇文给写完了,同时也做完了这几道题。

下面来做个总结。

A)顺着题意写代码是本能,但是需要考虑时间复杂度的问题,求模操作需要耗费大量时间,可以考虑通过构造约数而不是寻找约数来减少时间开销;

B)按照本能写代码,庆幸时间未超出范围;

C)本质上是一个简单数学题;

D)这是一个题目都看不懂的题,找了许多资料,终于知道要求了。求路径还是需要搜索算法,可以是BFS也可以是DFS,当然还需要考虑前向后向的问题。这个题在利用搜索算法求路径后,又增加了求字典序最小的路径,这又需要遍历可行路径求其最优解;

E)因为相同棋子相邻会合并成团,所以需要用到DFS求这个团周围的棋子分布,这个题让我觉得比较新奇的是对二维数组使用hash算法,计算数组hash值,通过比较hash值来判断当前棋盘局面与历史棋盘局面是否相同。

F)这又是一个我无法看出题意的题。在求上次优惠券操作后的最近一次未知操作的编号时,我自己写了一个二分查找用来查找不小于la的最小值下标。刚写完的时候做了测试,是可以用的,后来发现里几个比较大的问题,还花了挺多时间来调这个程序。主要是忽略t==la时的操作,还有缺少超出mark范围的判断。

参考:

1. 美团点评CodeM编程大赛|资格赛非官方题解
2.[题解] CodeM美团点评编程竞赛资格赛题
完整代码:https://github.com/ys0232/mycode/tree/master/codeCompete/src/CodeM

美团2017年CodeM资格赛题解相关推荐

  1. 2018美团codeM资格赛——python代码

    2018美团codeM资格赛--python代码 第一题:下单 题目描述 美团在吃喝玩乐等很多方面都给大家提供了便利.最近又增加了一项新业务:小象生鲜.这是新零售超市,你既可以在线下超市门店选购生鲜食 ...

  2. [编程题]大富翁游戏 美团2017 JAVA

    美团2017 JAVA [编程题]大富翁游戏 [编程题]拼凑钱币 [编程题]最大矩形面积 [编程题]最长公共连续子串 这道题限制了1≤n≤61\leq n\leq61≤n≤6,降低了问题难度,我首先想 ...

  3. CodeM资格赛B 锦标赛 题解

    题意 组委会正在为美团点评CodeM大赛的决赛设计新赛制. 比赛有 n 个人参加(其中 n 为2的幂),每个参赛者根据资格赛和预赛.复赛的成绩,会有不同的积分.比赛采取锦标赛赛制,分轮次进行,设某一轮 ...

  4. loj 6083.「美团 CodeM 资格赛」数码

    题目: 给定两个整数\(l\)和\(r\),对于任意\(x\),满足\(l\leq x\leq r\),把\(x\)所有约数写下来. 对于每个写下来的数,只保留最高位的那个数码.求\([1,9]\)中 ...

  5. loj 6085.「美团 CodeM 资格赛」优惠券

    题目: 一个有门禁的大楼,初始时里面没有人. 现在有一些人在进出大楼,每个人都有一个唯一的编号.现在有他们进出大楼的记录,但是有些被污染了,只能知道这里有一条记录,具体并不能知道. 一个人只有进大楼, ...

  6. 美团codeM资格赛 优惠券

    优惠券 时间限制:1秒 空间限制:32768K 美团点评上有很多餐馆优惠券,用户可以在美团点评App上购买.每张优惠券有一个唯一的正整数编号.当用户在相应餐馆就餐时,可以在餐馆使用优惠券进行消费.优惠 ...

  7. CodeM资格赛C 优惠券 题解

    题意 美团点评上有很多餐馆优惠券,用户可以在美团点评App上购买.每张优惠券有一个唯一的正整数编号.当用户在相应餐馆就餐时,可以在餐馆使用优惠券进行消费.优惠券的购买和使用按照时间顺序逐行记录在日志文 ...

  8. CodeM资格赛D 送外卖 题解

    题意 n 个小区排成一列,编号为从 0 到 n-1 .一开始,美团外卖员在第0号小区,目标为位于第 n-1 个小区的配送站. 给定两个整数数列 a[0]~a[n-1] 和 b[0]~b[n-1] ,在 ...

  9. CodeM资格赛A 音乐研究 题解

    题意 美团外卖的品牌代言人袋鼠先生最近正在进行音乐研究.他有两段音频,每段音频是一个表示音高的序列.现在袋鼠先生想要在第二段音频中找出与第一段音频最相近的部分. 具体地说,就是在第二段音频中找到一个长 ...

最新文章

  1. 笑哭了,科研版《后浪》,那些人类积攒了几百年的文献,像是人类专门为你们准备的礼物...
  2. 第四章 SQL*plus介绍、常用命令
  3. os.path.join的妙用
  4. linux中mbr最大多少分区,Linux与磁盘分区介绍(MBR,GPT)
  5. android入门程序源代码,安卓程序开发入门
  6. 30条值得程序员借鉴的好习惯
  7. 【xargs使用】查询包含某字符串的所有文件
  8. zabbix监控iptables防火墙状态之是否有丢弃的包(攻-击)
  9. linux运维常见英文报错中文翻译(菜鸟必知)
  10. Prim POJ 2031 Building a Space Station
  11. mysql嵌套查询语句
  12. java graphics类详解_java 基础二 Graphics类
  13. wordpress Avada主题banner制作
  14. python应用开发实战第一章 兽人之袭0.0.1
  15. duffing matlab,duffing方程matlab
  16. Linux iperf 用法介绍
  17. File转MultipartFile格式
  18. Pyinstaller打包exe附带图片的方法
  19. 正弦信号发生器设计制作经验教训
  20. 计算机保护分区,磁盘显示为GPT(保护分区)

热门文章

  1. 什么是WEB服务器、应用服务器
  2. 分享一个properties转YAML小工具
  3. ATFX:新西兰联储加息50基点,NZDUSD先涨后跌
  4. php源文件怎么打开,php文件怎么打开?
  5. 用html5如何编写星球运动,HTML5---Canvas-模拟地球-星球运行轨道
  6. 小明酱的算法实习生面试准备
  7. 2021年所有干货文章导航!#内有惊喜
  8. ripro9.2明文修正版,无加密无后门,无需扩展(亲测)虚拟机可用
  9. pgr_createTopology
  10. linux磁盘相关命令,Linux 磁盘相关命令整理