2022年4月23日美团笔试

1. 考试座位

题目描述:

现在有n个人坐成一排进行上机考试。但他们有的使用C语言,用C表示;而有的使用Java,用J表示。为了防止他们“友好交流”,小美老师要求任意座位相邻的两人之间使用的语言是不同的。小美每次可以交换相邻两人的位置,现在她想知道最少交换多少次可以满足要求?

输入描述:

输入一个整数n(1≤n≤106) ,表示有n个人。
然后输入n个字母c1,c2,… ,cn(ci∈{C,J})构成的字符串,表示每个人使用的语言。

输出描述:

输出一个整数S,表示最少需要交换S次。若不可能满足要求,则输出-1。

样例输入:

4
CCJJ

样例输出:

1

思路:
刚开始我的想法是双指针从前往后遍历就行,只要出现前后一样的,慢指针停下来,快指针到后面去找跟慢指针指向内容不同的东西,然后将他经过fast减slow次交换到前面就行了。但是这个有个漏洞,就是我每次都是从后往前搬运东西的,这个肯定不是最优的,就比如评论里的JCJCJCJCC,按照之前快慢双指针,就求出结果为9,但实际上只要进行4次就行了。所以我又想了想,或许我们可以采取另一种思路。我就规定第一个人是J,那么最后一个肯定是xx(根据C和J的人数差来决定)。我还是双指针的思路。我两头同时采取贪心的思路,寻找最优满足当前位置的人的移动次数。
是不是有点晕了?看代码,你就能理解了。

代码实现:

public class Main {
    static int res = 0;

    public static void main(String[] args) {
        //----------------输入------------------//
        Scanner scanner = new Scanner(System.in);
        int n = Integer.parseInt(scanner.nextLine());
        String str = scanner.nextLine();

        System.out.println(exChangeTime(str, n));
    }

    public static int exChangeTime(String str, int n) {
        int c = 0;
        int j = 0;
        char[] chs = str.toCharArray();
        for (int i = 0; i < n; i++) {
            if (chs[i] == 'C') {
                c++;
            } else {
                j++;
            }
        }
        int abs = c - j;
        if (abs == 0) {
            //说明两个一样多
            int pre = start_end(chs, n, 0, n - 1, 'J', 'C');
            int next = start_end(chs, n, 0, n - 1, 'C', 'J');
            return pre < next ? pre : next;
        } else if (abs == 1) {
            //说明C比J多
            return start_end(chs, n, 0, n - 1, 'C', 'C');
        } else if (abs == -1) {
            return start_end(chs, n, 0, n - 1, 'J', 'J');
        } else {
            return -1;
        }
    }

    public static int start_end(char[] chs, int n, int start, int end, char first, char last) {
        if (end - start <= 1) {
            return res;
        }
        for (int i = start; i <= end; i++) {
            if (chs[i] == first) {
                res = res + (i - start);
                //-----交换-------//
                char temp = chs[i];
                chs[i] = chs[start];
                chs[start] = temp;
                break;
            }
        }
        if (first == 'C') {
            first = 'J';
        } else {
            first = 'C';
        }

        for (int i = end; i >= start; i--) {
            if (chs[i] == last) {
                res = res + (end - i);
                //-----交换-------//
                char temp = chs[i];
                chs[i] = chs[end];
                chs[end] = temp;
                break;
            }
        }
        if (last == 'C') {
            last = 'J';
        } else {
            last = 'C';
        }
        return start_end(chs, n, start + 1, end - 1, first, last);
    }
}

2. 小美炒股

题目描述:

小美最近在炒股。

小美只关注一支股票,而且她有着预测未来的能力,她提前知道了未来n天的股票价格(第 i 天为ai)。

小美每天只在收盘时的瞬间操作,所以可以认为每天股票价格是不变的。在股市出现剧烈波动时,小美可能赚得过多,为了防止泄露超能力,她决定在总资金超过1e6=1000000之后不再买入(但是卖出时间不定,仍要求卖出时有最大收益)。超过100万后,如果她还想买入,可以捐款一些资金,使自己的资金不超过100万,这样她可以在超过100万前继续买入。(即资金超过100万后,找以100万为本金的单次买卖的最大收益)

现在小美现在有1000元,她想知道如果一直最优抉择,她在n天后拥有多少钱?请你帮帮她。(允许不买任意股票,购入股票数必须为整数)

输入描述:

对于每一组数据,第一行一个正整数n;
第二行n个空格隔开的整数 a1,a2, … ,an
1≤n≤10000 ,1≤ai≤1000

输出描述:

输出一个整数,表示小美最优决策后的金钱。

样例输入:

5
101 102 100 101 103

样例输出:

1039

思路:
原本想着用动态规划dp数组,但是这个玩意儿涉及本金,还涉及手头不能超过100万。着实受不了,所以我直接用回溯法了,如果大家有好的想法,欢迎告诉我呀。秋梨膏=.=

标准版:

public class Main {
    static int up = 1000000;

    public static void main(String[] args) {
        //----------------输入------------------//
        Scanner scanner = new Scanner(System.in);
        int n = Integer.parseInt(scanner.nextLine());
        String str = scanner.nextLine();

        //----------------整理输入------------------//
        //因为我只会在波峰波谷的地方操作,所以我只记录波峰波谷就行,这么好用的预见未来能里肯定要好好用啊
        List<Integer> list = new ArrayList<>();
        int first = Integer.parseInt(str.split(" ")[0]);
        int mid = Integer.parseInt(str.split(" ")[1]);
        if (first < mid) {
            list.add(first);
        }
        int flag = mid - first;
        for (int i = 1; i < n; i++) {
            int pre = Integer.parseInt(str.split(" ")[i - 1]);
            int next = Integer.parseInt(str.split(" ")[i]);
            if (flag * (next - pre) < 0) {
                list.add(pre);
                flag = 0 - flag;
            }
        }
        int last = Integer.parseInt(str.split(" ")[n - 1]);
        if (Integer.parseInt(str.split(" ")[n - 2]) < last) {
            list.add(last);
        }

        //TODO
        int[] prices = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            prices[i] = list.get(i);
        }

        System.out.println(Math.max(dynamic(prices, 1, 1000 / prices[0], 1000 % prices[0]), 1000));
    }

    public static int dynamic(int[] prices, int start, int n, int money) {//prices为操作,start为从什么时候开始操作,n为当前持有的股票数
        int max = 0;
        if (start == prices.length - 1) {
            if (n > 0) {
                return money + n * prices[start];
            } else {
                return money;
            }
        }

        if (n > 0) {
            //此时可以卖也可以不卖
            max = Math.max(dynamic(prices, start + 1, n, money), dynamic(prices, start + 1, 0, money + prices[start] * n));
        } else {
            //此时可以买也可以不买
            if (money > up) {
                max = Math.max(dynamic(prices, start + 1, 0, money), dynamic(prices, start + 1, up / prices[start], up % prices[start]));
            } else {
                max = Math.max(dynamic(prices, start + 1, 0, money), dynamic(prices, start + 1, money / prices[start], money % prices[start]));
            }
        }
        return max;
    }
}

代码是不是看上去好乱,加上回溯,我都不知道到底哪里买入哪里卖出的。没事,我这还有更强的杀招,什么时候买入什么时候卖出。我来告诉你:

详细版:

public class Main {
    static int up = 1000000;
    static List<String> outprint = new ArrayList<>();

    public static void main(String[] args) {
        //----------------输入------------------//
        Scanner scanner = new Scanner(System.in);
        int n = Integer.parseInt(scanner.nextLine());
        String str = scanner.nextLine();

        //----------------整理输入------------------//
        List<Integer> list = new ArrayList<>();
        int first = Integer.parseInt(str.split(" ")[0]);
        int mid = Integer.parseInt(str.split(" ")[1]);
        if (first < mid) {
            list.add(first);
        }
        int flag = mid - first;
        for (int i = 1; i < n; i++) {
            int pre = Integer.parseInt(str.split(" ")[i - 1]);
            int next = Integer.parseInt(str.split(" ")[i]);
            if (flag * (next - pre) < 0) {
                list.add(pre);
                flag = 0 - flag;
            }
        }
        int last = Integer.parseInt(str.split(" ")[n - 1]);
        if (Integer.parseInt(str.split(" ")[n - 2]) < last) {
            list.add(last);
        }

        //TODO
        int[] prices = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            prices[i] = list.get(i);
        }
        int dy = dynamic(prices, 1, 1000 / prices[0], 1000 % prices[0]);
        if (dy > 1000) {
            outprint.add("在价格为" + prices[0] + "时买入");
        }
        Collections.reverse(outprint);
        outprint.remove(outprint.size() - 1);
        outprint.add("最终手里有" + dy);
        System.out.println(outprint);
    }

    public static int dynamic(int[] prices, int start, int n, int money) {//prices为操作,start为从什么时候开始操作,n为当前持有的股票数
        int max = 0;
        if (start == prices.length - 1) {
            if (n > 0) {
                int res = money + n * prices[start];
                outprint.add("在价格为" + prices[start] + "时卖出");
                return res;
            } else {
                return money;
            }
        }

        if (n > 0) {
            //此时可以卖也可以不卖
            int no = dynamic(prices, start + 1, n, money);
            int yes = dynamic(prices, start + 1, 0, money + prices[start] * n);
            if (no < yes) {
                outprint.add("在价格为" + prices[start] + "时卖出");
                max = yes;
            } else {
                max = no;
            }
        } else {
            //此时可以买也可以不买
            if (money > up) {
                int no = dynamic(prices, start + 1, 0, money);
                int yes = dynamic(prices, start + 1, up / prices[start], up % prices[start]);
                if (yes > no) {
                    outprint.add("在价格为" + prices[start] + "时买入" + "此时因为手头超过100万了,所以我捐了" + (money - up));
                    max = yes;
                } else {
                    max = no;
                }
            } else {
                int no = dynamic(prices, start + 1, 0, money);
                int yes = dynamic(prices, start + 1, money / prices[start], money % prices[start]);
                if (yes > no) {
                    outprint.add("在价格为" + prices[start] + "时买入");
                    max = yes;
                } else {
                    max = no;
                }
            }
        }
        return max;
    }
}

3. 颜色排序

题目描述:

小美得到了一个长度为n的整数序列,并且序列上每个数字都被染上了颜色1~n的其中一种。现在小美想要给这个序列按从小到大排序,但她每次操作只能交换相邻两个数,并且这两个数的颜色要不相同。她想知道进行若干次操作之后能不能给这个序列排好序。

输入描述:

第一行一个正整数T,表示有T组数据。

对于每一组数据,第一行一个正整数n,表示这个序列的长度;第二行n个正整数ai,表示该序列;第三行n个正整数ci,表示第 i 个数的颜色。

数字间两两有空格隔开

1≤T≤8,1≤n≤104 ,1≤ai,ci≤n

输出描述:

对于每一组数据,如果可以排好序,输出一行Yes;否则,输出一行No。

样例输入:

2
5
3 2 4 1 5
1 2 2 3 1
3
2 2 1
1 1 1

样例输出:

Yes
No

样例解释:

第一组样例可以如下排序:
[3 2 4 1 5] -> [2 3 4 1 5] -> [2 3 1 4 5] -> [2 1 3 4 5] -> [1 2 3 4 5]

思路:同色不能交换的意思也就是说同色的相对顺序不能改变,如此一来我枚举每一种颜色, 如果相同颜色的数字都是有序的,那么就返回Yes,否则返回No。

public class Main {
    public static void main(String[] args) {
        //------------------输入--------------------//
        Scanner scanner = new Scanner(System.in);
        int T = Integer.parseInt(scanner.nextLine());
        for (int i = 0; i < T; i++) {
            int n = Integer.parseInt(scanner.nextLine());
            int[] nums = new int[n];
            String str1 = scanner.nextLine();
            for (int j = 0; j < n; j++) {
                nums[j] = Integer.parseInt(str1.split(" ")[j]);
            }
            int[] colors = new int[n];
            String st2 = scanner.nextLine();
            for (int j = 0; j < n; j++) {
                colors[j] = Integer.parseInt(st2.split(" ")[j]);
            }
            //TODO
            System.out.println(colorrank(nums, colors, n));
        }
    }

    public static String colorrank(int[] nums, int[] colors, int n) {
        //对于某个颜色,必须是有序的
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            if (map.containsKey(colors[i])) {
                if (map.get(colors[i]) > nums[i]) {
                    return "No";
                } else {
                    map.put(colors[i], nums[i]);
                }
            } else {
                map.put(colors[i], nums[i]);
            }
        }
        return "Yes";
    }
}

4. 方格染色

题目描述:

小美从老师那里得到了一张N x M的方格纸(即N行M列),上面每个方格都染上了一种颜色。

老师又给了小美另一张大小一模一样但是没有染色的方格纸,并对于方格纸上的每种颜色,老师给了她一些大小为2 x 2的颜料笔,每次可以选择一个大小恰好为2 x 2的方格染成同一种颜色(对已染色的方格也可以染色,同一种颜料笔可以用多次)。

老师想考考小美:能不能用这些颜料笔对这张未染色的方格纸进行染色,使得其与已染色的这张方格纸一模一样?

输入描述:

第一行一个正整数T,表示有T组数据。

对于每一组数据,第一行输入两个正整数N,M;

接下来N行,每行M个数,第 i 行第 j 列的数表示为 Cij,表示这个位置的方格的颜色。

数字间两两有空格隔开。

2≤N,M≤200 , 1≤Cij≤NM , 1≤T≤10

输出描述:

对于每一组数据,如果可以染成与已染色的方格纸相同的话,输出一行Yes;否则,输出一行No。

样例输入:

2
4 4
5 5 3 3
1 1 5 3
2 2 5 4
2 2 4 4
3 4
1 1 1 1
2 2 3 1
2 2 1 1

样例输出:

Yes
No

思路:
染色的题目一般倒着做, 我最后一次涂抹的地方肯定是一块完整的2*2的同色快。那么我倒着来,我把它擦了(都换成-1)。然后我重新遍历,只要是一个2*2里出现同色块或者没有颜色(-1),那说明我之前在这里涂抹过,我把他们都擦掉。循环往复。最后如果我看整张纸是白纸了,那说明确实我可以画出来。只要还有颜色,直接返回No

代码实现:

public class Main {
    public static void main(String[] args) {
        //-------------输入-------------------//
        Scanner scanner = new Scanner(System.in);
        int T = Integer.parseInt(scanner.nextLine());
        for (int i = 0; i < T; i++) {
            String str1 = scanner.nextLine();
            int n = Integer.parseInt(str1.split(" ")[0]);
            int m = Integer.parseInt(str1.split(" ")[1]);
            int[][] colors = new int[n][m];
            for (int j = 0; j < n; j++) {
                String str2 = scanner.nextLine();
                for (int k = 0; k < m; k++) {
                    colors[j][k] = Integer.parseInt(str2.split(" ")[k]);
                }
            }
            System.out.println(give_it_color_see(colors, n, m));
        }
    }

    public static String give_it_color_see(int[][] colors, int n, int m) {
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < m - 1; j++) {
                //将[i,j]作为左上角
                int color = 0;
                if (colors[i][j] != -1) {
                    color = colors[i][j];
                } else if (colors[i + 1][j] != -1) {
                    color = colors[i + 1][j];
                } else if (colors[i][j + 1] != -1) {
                    color = colors[i][j + 1];
                } else if (colors[i + 1][j + 1] != -1) {
                    color = colors[i + 1][j + 1];
                }
                if (color == 0) {
                    continue;
                }
                if ((colors[i][j] == color || colors[i][j] == -1) && (colors[i][j + 1] == color || colors[i][j + 1] == -1) && (colors[i + 1][j] == color || colors[i + 1][j] == -1) && (colors[i + 1][j + 1] == color || colors[i + 1][j + 1] == -1)) {
                    colors[i][j] = -1;
                    colors[i][j + 1] = -1;
                    colors[i + 1][j] = -1;
                    colors[i + 1][j + 1] = -1;
                    i = 0;
                    j = -1;
                }
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (colors[i][j] != -1) {
                    return "No";
                }
            }
        }
        return "Yes";
    }
}

其他

Java的二维数组打印方式:
用Arrays.deepToString()

public class Main {
    public static void main(String[] args) {
        int[][] array = {{1, 2, 3}, {4, 5, 6}};
        System.out.println(Arrays.toString(array));//打印的是地址
        System.out.println(Arrays.deepToString(array));
    }
}

本人愚笨,上述的代码也不知道是否正确,如果有发现错误或者有更好的方法,请评论,谢谢各位大哥大嫂了

特别鸣谢

  • qq_41556048:第一题错了,JCJCJCJCC 这种情况最小应该是4 你的是9 错了
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

走出半生仍是少年

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

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

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

打赏作者

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

抵扣说明:

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

余额充值