小米10.12笔试编程题

1:给你一个长度为n的数组,你可以对0~(n-1)内任意位置 i 进行取相反数操作,但arr[i]取反后,arr[i+1]也会跟着取反。你可以进行任意次这样的操作。请返回反转后arr数组之和的最大值。

思路

一道dp题(本人菜鸡只会用记忆化搜索)

定义dp【i】【0】为i位置 未被上一个数取反时 i~(n-1)任意选择反转。

定义dp【i】【1】为i位置 被上一个数取反时 i~(n-1)任意选择反转。

dp【i】【0】= max ( dp【i+1】【0】+arr[i] , dp【i+1】【1】-arr[i] );

dp【i】【1】= max ( dp【i+1】【0】-arr[i] , dp【i+1】【1】+arr[i] );

那么结果为dp【0】【0】

上代码:

import java.util.Scanner;

public class Main {
    static int maxn = 30000 + 10;
    static long maxi = (int) (1e9 + 10);
    static long[] arr = new long[maxn];
    // 定义动态规划 dp 数组,dp[i][0] 表示第 i 个元素不取反的最大和,dp[i][1] 表示第 i 个元素取反的最大和
    static Long[][] dp = new Long[maxn][2];
    static int n;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextLong();
        }
        System.out.println(dfs(0, 0));
    }

    // 定义 dfs 函数,cur 表示当前遍历到的数组下标,not 表示当前元素是否取反(0 表示不取反,1 表示取反)
    public static long dfs(int cur, int not) {
        // 如果 dp[cur][not] 已经计算过,直接返回结果
        if (dp[cur][not] != null) {
            return dp[cur][not];
        }
        // 根据 not 值决定当前元素是否取反
        long currValue = not == 0 ? arr[cur] : -arr[cur];
        // 如果当前是最后一个元素,则返回其值(处理边界情况)
        if (cur == n - 1) return currValue;
        // 递归计算当前元素不取反的情况下,后续元素的最大和
        long noNot = dfs(cur + 1, 0) + currValue;
        // 递归计算当前元素取反的情况下,后续元素的最大和
        long isNot = dfs(cur + 1, 1) - currValue;
        // 取不取反两种情况中的较大值作为 dp[cur][not] 的值
        dp[cur][not] = Math.max(noNot, isNot);
        return dp[cur][not];
    }
}

附带一句:笔者笔试的时候第一眼以为是翻硬币 模板,以为自己模板记错了,疯狂想怎么递推。笔试完突然想到应该dp的😭😭😭。

再加一道网友的思路:如果负数个数为偶数,那么最终可以把全部反转成正数,如果是奇数,最后则只会剩下一个负数。并且这个负数可以是任意一个位置。那么最终结果为:负数个数==偶数? sum( arr) : sum( arr ) - 2*绝对值最小的数。

    public static long ans(int n, long[] arr) {
        int cnt = 0, min = Integer.MAX_VALUE;
        long sum = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] < 0) cnt++;
            long abs = Math.abs(arr[i]);
            min = (int) Math.min(min, abs);
            sum += abs;
        }
        if (cnt % 2 == 0) {
            return sum;
        } else {
            return sum - min * 2L;
        }
    }

题目2:给你一个一串长度为 n的 手链(不是环),手链上的珠子编号从1到n。

它可能有多次如下操作:将 a位置的珠子取出来放到 b位置的前面或后面。

问操作q次后,这串手链的最终编号。

lru缓存算法翻版:如果不了解这个算法的话可以先去了解下。

ac代码:注释是找ai加的

import java.util.Scanner;

public class Main {
    static int maxn = (int) (1e5 + 10);
    // 定义一个 Node 数组,表示手链上的每个珠子(节点)
    static Node[] nodes = new Node[maxn];
    static int n, q;

    /**
     * lru算法,使用双向链表来模拟手链的操作,使用map集合来快速查找珠子。(珠子的值很小所以我用数组代替了(nodes))
     *
     * @param args
     */
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        q = sc.nextInt();

        // 初始化头节点 h 和尾节点 t,h 和 t 是哨兵节点,方便操作
        Node h = new Node(-1);  // 头节点(不属于手链上的珠子)
        Node t = new Node(-1);  // 尾节点(不属于手链上的珠子)
        h.next = t;
        t.pre = h;

        // 初始化手链中的每个珠子,构建一个双向链表
        for (int i = 1; i <= n; i++) {
            Node node = new Node(i);  // 创建第 i 个珠子
            node.pre = t.pre;         // 珠子的 pre 指向尾节点的前一个节点
            node.next = t;            // 珠子的 next 指向尾节点
            t.pre.next = node;        // 尾节点的前一个节点的 next 指向该珠子
            t.pre = node;             // 尾节点的 pre 更新为该珠子
            nodes[i] = node;          // 将珠子存入 nodes 数组
        }

        // 处理 q 次操作
        for (int i = 0; i < q; i++) {
            int a = sc.nextInt();     // 珠子 a 的编号
            int b = sc.nextInt();     // 珠子 b 的编号
            int op = sc.nextInt();    // 操作类型,0 表示将 a 插入到 b 前面,1 表示将 a 插入到 b 后面

            Node nodeA = nodes[a];    // 获取珠子 a 的节点
            Node nodeB = nodes[b];    // 获取珠子 b 的节点

            // 从链表中移除珠子 a
            nodeA.pre.next = nodeA.next; // 珠子 a 的前一个节点的 next 指向珠子 a 的下一个节点
            nodeA.next.pre = nodeA.pre;  // 珠子 a 的下一个节点的 pre 指向珠子 a 的前一个节点

            // 根据 op 的值,选择将珠子 a 插入到 b 的前面或后面
            if (op == 0) {  // 将 a 插入到 b 的前面
                nodeA.next = nodeB;           // 珠子 a 的 next 指向珠子 b
                nodeA.pre = nodeB.pre;        // 珠子 a 的 pre 指向珠子 b 的前一个节点
                nodeB.pre.next = nodeA;       // 珠子 b 的前一个节点的 next 指向珠子 a
                nodeB.pre = nodeA;            // 珠子 b 的 pre 更新为珠子 a
            } else {  // 将 a 插入到 b 的后面
                nodeA.next = nodeB.next;      // 珠子 a 的 next 指向珠子 b 的下一个节点
                nodeA.pre = nodeB;            // 珠子 a 的 pre 指向珠子 b
                nodeB.next.pre = nodeA;       // 珠子 b 的下一个节点的 pre 指向珠子 a
                nodeB.next = nodeA;           // 珠子 b 的 next 更新为珠子 a
            }
        }

        // 输出操作后的手链
        Node curr = h.next;  // 从头节点 h 的下一个节点开始遍历
        while (curr != t) {  // 遍历到尾节点 t 停止
            System.out.print(curr.v + " ");  // 输出当前珠子的编号
            curr = curr.next;                // 移动到下一个珠子
        }
    }

    // 定义双向链表节点类 Node
    static class Node {
        int v;       // 珠子的编号
        Node pre;    // 指向前一个节点的指针
        Node next;   // 指向下一个节点的指针

        // 构造函数,初始化编号 v
        public Node(int v) {
            this.v = v;
        }

        // 构造函数,初始化编号 v 以及前一个节点和下一个节点的指针
        public Node(int v, Node pre, Node next) {
            this.v = v;
            this.pre = pre;
            this.next = next;
        }
    }
}

#小米笔试#
全部评论
第一道很简单,负数的个数是偶数,则可以通过置换获得全为正数,如果是奇数个数,则数组里面有一个必须保持负数,直接那那个最小的变成负数,其他全正,加起来就行ak,第二道直接arraylist做,但是只a了82
2 回复 分享
发布于 10-12 19:22 广东
啊啊啊啊啊啊,好气好气。第一题要睡不着觉了😭😭😭
点赞 回复 分享
发布于 10-12 18:53 新加坡
顺便问下大佬们,小米笔试是acm赛制还是ioi赛制
点赞 回复 分享
发布于 10-12 18:53 新加坡
第一题贪心, 1计算数字里0的个数和负数的个数 2计算所有数字绝对值之和 3如果0的个数大于0个或者负数个数是偶数则返回之前的和 否则减去最小的绝对值*2 ,输出就行
点赞 回复 分享
发布于 10-13 12:26 广东
楼主楼主 这个是前面选择题加最后两个coding吗
点赞 回复 分享
发布于 昨天 13:21 安徽

相关推荐

3 6 评论
分享
牛客网
牛客企业服务