fintech

1 金字塔最多金币

链接:https://ac.nowcoder.com/acm/contest/5246/A
来源:牛客网
题目描述
小招在玩一款游戏:在一个N层高的金字塔上,以金字塔顶为第一层,第i层有i个落点,每个落点有若干枚金币,在落点可以跳向左斜向下或向右斜向下的落点。若知道金字塔的层数N及每层的金币数量分布,请计算小招在本次游戏中可以获得的最多金币数量。
输入描述:
输入共有N + 1行(N ≤ 1024),第一行为高度N,第二行至N + 1行 ,为该金字塔的金币数量分布。
输出描述:
输出金币数量。
示例1
输入
复制
5
8
3 8
8 1 0
4 7 5 4
3 5 2 6 5
输出
复制
31

开始没看懂题目,样例怎么算都是34,不是31啊?
后来看到这是个金字塔,虽然输入的是个直角三角形,其实应该是个等腰三角形:

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

这样改了代码之后就没问题了

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        long[] b = new long[N*(N+1)/2];
        int count = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j <= i; j++) {
//                b[count++] = in.nextLong();
                b[count++] = new Random().nextInt(20);

            }
        }
//        System.out.println(Arrays.toString(b));
        System.out.println(maxOfCoins(b,N));
        System.out.println(maxOfCoins2(b,N));
        in.close();
    }

    private static long maxOfCoins(long[] b, int n) {
        long[][] p = new long[n][n];
        int count = 0;
        p[0][0] = b[count++];
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                count = i*(i+1)/2+j;
                long temp = 0;

                if(j==0) {
                    p[i][j] = b[count++]+ p[i-1][j];
                    continue;
                }
                if(j==i) {
                    p[i][j] = b[count++]+  p[i-1][j-1];
                    continue;
                }
                temp = Math.max(p[i-1][j],p[i-1][j-1]);
                p[i][j] = temp + b[count++];
            }
        }
        long max = 0;
        for (int i = 0; i < n; i++) {
            max = Math.max(p[n-1][i],max);
        }
        return max;
    }
}

看了别人的代码,这题从底层往上动态规划更简单。
原C代码改成了java的

private static long maxOfCoins2(long[] b, int n) {
        long[][] a = new long[n][n];
        long[][] f = new long[n][n];
        int count = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                a[i][j] = b[count++];
            }
        }
        for (int j = 0; j <n; j++) {
            f[n-1][j] = a[n-1][j];
        }
        for (int i = n-2; i >=0; i--) {
            for (int j = 0; j <=i; j++) {
                f[i][j] = a[i][j]+Math.max(f[i+1][j],f[i+1][j+1]);
            }
        }
        return f[0][0];
    }

2.情侣牵手

链接:https://ac.nowcoder.com/acm/contest/5246/B
来源:牛客网
题目描述
在一场集体婚礼上,有n对新人需要坐在连续排列的 2n个座位上合影,同一对新人彼此挨着。由于进场时各对新人并未按序入座,请计算最少交换座位的次数,以便使每对新人均可并肩坐在一起。一次交换可选择任意两人,让他们互换座位。
全部新人的序号可用 0 到 2n-1 的整数表示,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2n-2, 2n-1)。
row[i]指最初坐在第 i 个座位上的新人编号,i是从0到(2n-1)的一个升序全排列,row不存在重复值。
输入描述:
输入共有2行,第一行为n,即共有多少对新人(2≤n≤100000 ),第二行为row,即2n个座位上的初始新人编号。
输出描述:
输出最少交换座位的次数。
示例1
输入
复制
2
0 2 1 3
输出
1
说明
我们只需要交换1和2的位置即可
示例2
输入
2
3 2 0 1
输出
0
说明
无需交换座位,所有的新人都已经并肩坐在一起

leetcode765相同

方法一:贪心(通过率95%)

第9行的continue忘了写,我自己都不敢信,debug好久。。。还是太菜

private static int numOfSwap(int[] pair, int n) {
        int i=0;
        int j ;
        int sum = 0;
        while (i<2*n){
            int target = pair[i]^0x01;
            if (pair[i+1]==target){
                i+=2;
                continue;
            }
            j=i;
            while (++j<2*n){
                if (pair[j]==target){
                    pair[j] = pair[i+1];
                    pair[i+1] = target;
                    sum++;
                    break;
                }
            }

            i+=2;
        }
        return sum;
    }

方法二:循环搜索

官方题解的方法2(方法1回溯超时没看,方法3是贪心)

思路 假设 ”快乐交换理论“
是对的,如果一对情侣不坐在一对沙发上,那么就有两种交换的选择将这张沙发上凑成一对情侣。对于这样的交换,其实可以把它化成一张图,举个例子,有沙发
X 和 沙发 Y(可能是同一个沙发),可以通过画一条 X 到 Y 的无向边来表示 X 沙发跟 Y
沙发上可以组成一对情侣。这样最后画成的图把它称作情侣图,这张图每个节点的度为2,图上有一些连通分量。

每次将一个沙发上凑成一对情侣之后,在图上的变化是多了一个自循环的连通分量。我们的目标是让图中有 N
个连通分量,每个连通分量代表一对情侣。每次交换都会将连通分量的数量增加 1,根据 ”快乐交换理论“,问题的答案可以通过 N
减去最开始情侣图中连通分量的个数来得到。

现在来证明 ”快乐交换理论“
是正确的,可以观察到不论怎么交换,一次交换都不可能增加多于一个连通分量,所以每次增加一个连通分量的交换就是最优解。(同时这也可以证明方法一中的假设,因为不管按什么顺序去增加连通分量,结果都不会改变)

算法

首先来构建图,定义 adj[node] 为当前 node
的情侣所处沙发的下标。随后找到所有的联通分量。最后让每个沙发变成一个自循环的连通分量。

class Solution {

 public int minSwapsCouples(int[] row) {
        int N = row.length / 2;
        //couples[x] = {i, j} means that
        //couple #x is at couches i and j (1 indexed)
        int[][] couples = new int[N][2];
        
        for (int i = 0; i < row.length; ++i)
            add(couples[row[i]/2], i/2 + 1);

        //adj[x] = {i, j} means that
        //x-th couch connected to couches i, j (all 1 indexed) by couples
        int[][] adj = new int[N+1][2];
        for (int[] couple: couples) {
            add(adj[couple[0]], couple[1]);
            add(adj[couple[1]], couple[0]);
        }

        // The answer will be N minus the number of cycles in adj.
        int ans = N;
        // For each couch (1 indexed)
        for (int r = 1; r <= N; ++r) {
            // If this couch has no people needing to be paired, continue
            if (adj[r][0] == 0 && adj[r][1] == 0)
                continue;

            // Otherwise, there is a cycle starting at couch r.
            // We will use two pointers x, y with y faster than x by one turn.
            ans--;
            int x = r, y = pop(adj[r]);
            // When y reaches the start 'r', we've reached the end of the cycle.
            while (y != r) {
                // We are at some couch with edges going to 'x' and 'new'.
                // We remove the previous edge, since we came from x.
                rem(adj[y], x);

                // We update x, y appropriately: y becomes new and x becomes y.
                x = y;
                y = pop(adj[y]);
            }
        }
        return ans;
    }

    // Replace the next zero element with x.
    public void add(int[] pair, int x) {
        pair[pair[0] == 0 ? 0 : 1] = x;
    }

    // Remove x from pair, replacing it with zero.
    public void rem(int[] pair, int x) {
        pair[pair[0] == x ? 0 : 1] = 0;
    }

    // Remove the next non-zero element from pair, replacing it with zero.
    public int pop(int[] pair) {
        int x = pair[0];
        if (x != 0) {
            pair[0] = 0;
        } else {
            x = pair[1];
            pair[1] = 0;
        }
        return x; 
     } } 

复杂度分析

时间复杂度: O(N)O(N),其中 NN 情侣对的数量。

空间复杂度: O(N)O(N)。

作者:LeetCode
链接:https://leetcode-cn.com/problems/couples-holding-hands/solution/qing-lu-qian-shou-by-leetcode/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法三:并查集(通过率100%)

leetcode用户题解:

解题思路 union find 情侣两两一组,如果row[i] 和 row[i + 1]
不原来不在一个组,就把它放入一个组里,每个组里面的个数都是偶数。

每当减少一个组了,就代表需要多交换一次。

代码

java class Solution {
    public int minSwapsCouples(int[] row) {
        int res = 0;
        int n = row.length;
        int[] root = new int[n];
        for (int i = 0; i < n; i ++) {
            root[i] = i;
        }
        for (int i = 0; i < n; i += 2) {
            int x = find(root, row[i] / 2);
            int y = find(root, row[i + 1] / 2);
            if (x != y) {
                root[x] = y;
                res ++;
            }
        }
        return res;
    }

    int find (int[] root, int idx) {
        while (root[idx] != idx) return find(root, root[idx]);
        return idx;
    } 
} 

作者:don-vito-corleone
链接:https://leetcode-cn.com/problems/couples-holding-hands/solution/java-union-find-by-don-vito-corleone/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.修塔游戏

链接:https://ac.nowcoder.com/acm/contest/5246/C 来源:牛客网

题目描述
小招正在玩一款修塔游戏:系统中有n座高塔,每座高塔由若干个高度相同的方块堆砌而成。修塔游戏的规则为:
(1)每次从最高塔的塔尖拿走一个方块
(2)每次在最低塔的塔尖堆砌一个方块
小招每次只能完成上述两个动作中的一个动作。游戏的目标是使n座高塔中至少有k座高塔的高度相同,请问小招最少需要多少次才能完成游戏。 输入描述:
输入共有2行,第一行为n和k(1≤k≤n≤200000 ),第二行为n座塔的高度组成的数组 a1, a2,
…an(1≤aj≤10000)。
输出描述: 输出值为最少需要多少次动作才能完成游戏。 示例1 输入 6 5 1 2 2 4 2 3 输出 3

参考题解:(有问题,通过率0)

思路主要是: (1)先排序,然后分两条路径。
(2)正向将第k个值以前的值都改成与第k个值相同的值。然后看第k个位置之后如果有和arr[k]相同的值的话,就是多走了,再减下去。
(3)反向亦然。

public class test03 {
   public static void main(String[] args) {
       Scanner scan=new Scanner(System.in);
       int N=scan.nextInt();
       int k=scan.nextInt();
       scan.nextLine();
       int [] arr=new int[N];
       for(int i=0;i<N;i++){
           arr[i]=scan.nextInt();
       }
       Arrays.sort(arr);
       int result = beginGame(arr, N, k);
       System.out.println(result);
   }

   private static int beginGame(int[] arr, int N, int k) {

       int path1 = 0;
       int path2 = 0;

       for (int i=0;i<k;i++){
           path1+=arr[k-1]-arr[i];
           path2+=arr[N-i-1]-arr[N-k];
       }

       int index1=k;
       int index2=N-k-1;
       while(index1<N&&arr[index1++]==arr[k-1]){
          path1--;
       }
       while(index2>=0&&arr[index2--]==arr[N-k]){
           path2--;
       }
       return Math.min(path1, path2);
   } }

版权声明:本文为CSDN博主「JAVA小摩托不堵车」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42924812/article/details/105836102

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值