字节跳动校招题

字节跳动校招题

链接:https://www.nowcoder.com/questionTerminal/448127caa21e462f9c9755589a8f2416

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

/**
*

  • https://www.nowcoder.com/question/next?pid=16516564&qid=362290&tid=28447145
  • 题设:总共有36张牌,每张牌是1~9。每个数字4张牌。
  • 你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌,和牌条件如下:
  • (1)14张牌中有2张相同数字的牌,称为雀头。
  • (2)除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。
  •  顺子的意思是:递增的连续3个数字牌(例如234,567等);
    
  •  刻子的意思是:相同数字的3个数字牌(例如111,777)
    
  • 输入:已有的13张牌
  • 输出:可以使14张牌成为和牌的最后一张(如果有多种可能,按从小到大的顺序输出)
  • 分析:由小到大添加可能的牌(如果原来牌组已经有了4张该种牌那肯定就不可能了)。
  • 难点是判定14张牌是否是和牌:
  •  第一步选择两张数字相同的,作为雀头;
    
  •  剩下的牌作为顺子的部分还是刻子部分?(从小到打遍历)
    
  •      如果除去雀头,一个数字有1或2张:那么这两张肯定作为刻子;
    
  •      如果除去雀头,一个数字有3张:那么这三张都是刻子或者都是顺子;
    
  •          假设该数字是顺子,直接划掉
    
  •          假设该数字是刻子,就划掉a,a+1,a+2
    
  •      如果除去雀头,一个数字有4张:那么肯定有1张作为刻子,其余三张都是刻子或者都是顺子;
    
  •          先划掉刻子的那张,     a,a+1,a+2
    
  •          剩下三张依照上述内容继续处理!
    
  • 分析到这里,不难看出采用回溯法可以解决。
  • 回溯除了本身是一种暴力实用的算法,还能有效帮助我们理解值传递和引用传递的区别!
  • tips:Java数组如何转化为集合?https://www.cnblogs.com/kangkaii/p/8427739.html
  • 个人喜欢new一个list,然后调用Collections.addAll(list,arr);
  • Arrays.asList()里面不能传数组引用竟然!!!必须要传数组的具体内容!

*/
public class Main {
private static int[] count= new int[9];
//由小到大存放可以使牌胡的最后一张(要有可能才行)
private static ArrayList getRes(Integer[] data){
//存储可行解
ArrayList res=new ArrayList<>();
//统计19出现的次数,count[i]统计的是i+1的次数。i从08
for (Integer e : data) {
count[e-1]++;
}
for (int i = 1; i <= 9; i++) {
//1.visit
if(count[i-1]<4){
count[i-1]++;//说明该张牌可以被插入
}else {
continue;
}
//2.operate:after copying
int[] countCp = Arrays.copyOf(count, count.length);
if( canFindQueTou(countCp) ){
res.add(i);
}
//3.backTrack
count[i-1]–;
}
return res;
}
//找雀头
private static boolean canFindQueTou(int[] count){
int cnt=0;//记录删掉的牌
for (int integer = 1; integer <= 9; integer++) {
if(count[integer-1]>=2){//找到可以作为雀头的integer
//1.visit
count[integer-1]-=2;
//2.operation
cnt+=2;
int[] countCp = Arrays.copyOf(count, count.length);
if(canGoOn(countCp)){//&&后面也能正常进行
return true;
}
//3.backTrack
count[integer-1]+=2;
}
}
return false;
}
//给其它牌配顺子或者刻子!
private static boolean canGoOn(int[] count) {
for (int i = 1; i <= 7; i++) {
if(count[i-1]==1||count[i-1]==2){//如果该牌只有1张或者两张,必然作为刻子
if(count[i]>=count[i-1]&&count[i+1]>=count[i-1]){
count[i]-=count[i-1];
count[i+1]-=count[i-1];
count[i-1]-=count[i-1];
continue;
}
return false;
}
if(count[i-1]==3){//如果该牌有3张,作为顺子是当前可行step;3张都作为刻子看情况
//能做刻子就做刻子,否则才做顺子?这种策略真的不会出错吗?
//有没有做顺子可以做刻子不行的情况?
//111 222 333 顺刻都行
//111 2222 3333 顺子:2222 3333 刻子: 2 3
if(count[i]>=count[i-1]&&count[i+1]>=count[i-1]){//3张都可以作为刻子的情形
count[i]-=count[i-1];
count[i+1]-=count[i-1];
count[i-1]-=count[i-1];
continue;
}else {//作为顺子
count[i-1]-=count[i-1];
continue;
}
// return false;
}
if(count[i-1]==4){//如果该牌有4张,1张作为刻子
if(count[i]>=1&&count[i+1]>=1){
count[i]-=1;
count[i+1]-=1;
count[i-1]-=1;
i–;//继续当前牌的判断,退役到上头该牌有3张的那种情况了
continue;
}
return false;
}
}
return (count[7]==0||count[7]==3)&&(count[8]==0||count[8]==3);
}

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    while (sc.hasNext()) { //分隔符:换行||空格
        Integer[] data=new Integer[13];
        for (int i = 0; i < 13; i++) {
            data[i]=Integer.parseInt(sc.next());
        }
        ArrayList res = getRes(data);
        if(res.size()==0){
            System.out.print(res.size());
        }else {
            for (int i = 0; i < res.size(); i++) {
                System.out.print(res.get(i)+((i!=res.size()-1)?" ":""));
            }
        }
    }
}

}

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw =30x30

你可能感兴趣的:(字节跳动校招题)