欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【左神算法】有一排正数,玩家A和玩家B都可以看到。 每位玩家在拿走数字的时候,都只能从最左和最右的数中选择一个。 玩家A先拿,玩家B再拿,两人交替拿走所有的数字, 两人都力争自己拿到的数的总和比对方多

程序员文章站 2022-07-07 11:12:20
...

1.问题

有一排正数,玩家A和玩家B都可以看到。
每位玩家在拿走数字的时候,都只能从最左和最右的数中选择一个。
玩家A先拿,玩家B再拿,两人交替拿走所有的数字,
两人都力争自己拿到的数的总和比对方多。请返回最后获胜者的分数。
例如:
5,2,3,4
玩家A先拿,当前他只能拿走5或者4。
如果玩家A拿走5,那么剩下2,3,4。轮到玩家B,此时玩家B可以选择2或4中的一个,…
如果玩家A拿走4,那么剩下5,2,3。轮到玩家B,此时玩家B可以选择5或3中的一个,…

2.思路&code

2.1 win1()思路

win1():由于存在AB玩家都可以看到数字的大小,所以每次都在做一个决策。先开始的玩家势必会拿最大数,而第二玩家势必只能在第一个玩家拿到最大的前提下,自己去拿剩下数字中的最大者。所以用递归就可以很清晰的表达。
1.针对谁先拿必然是先拿最大者,而后者就是拿最次大者。依序就是 first->second->first—>second …
a.其中 当i == j 对于first来说 可以拿到arr[i]
b.i == j 对于second来说 拿不到arr[i] 因为first具有优先权。

	//1.针对谁先拿必然是先拿最大者,而后者就是拿最小者。依序就是 first->second->first—>second .....
    // a.其中 当i == j 对于first来说 可以拿到arr[i]
    // b.i == j 对于second来说 拿不到arr[i] 因为first具有优先权。
    public static int win1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        return Math.max(first(arr, 0, arr.length - 1), second(arr, 0, arr.length - 1));
    }

    public static int first(int[] arr, int i, int j) {
        if (i == j) {
            return arr[i];
        }
        return Math.max(arr[i] + second(arr, i + 1, j), arr[j] + second(arr, i, j - 1));
    }

    public static int second(int[] arr, int i, int j) {
        if (i == j) {
            return 0;
        }
        return Math.min(arr[i] + first(arr, i + 1, j), arr[j] + first(arr, i, j - 1));
    }

2.2 win2思路

win2()思路
win1中递归会存在大量的重复计算。因此我们可以改成dp。dp主要是基于已知的条件来对下一项进行选择。而first玩家的决策,会影响到second玩家,因此。需要二维dp来解决这个问题。first[][] 记录first玩家的决策路径,second[][]记录second玩家的决策路径。i记录行号,j记录列。i的值不可能大于j 所以左下三角是没有数据的。

	public static int win2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        //初始化状态  dp方程
        int[][] first = new int[arr.length][arr.length];
        int[][] second = new int[arr.length][arr.length];

        for (int j = 0; j < arr.length; j++) {
            //初始化参数
            first[j][j] = arr[j];
            for (int i = j-1; i >=0 ; i--) {
                //进行决策
                first[i][j] = Math.max(arr[i]+second[i+1][j],arr[j]+second[i][j-1]);
                second[i][j] = Math.max(first[i+1][j],first[i][j-1]);
            }
        }
        return Math.max(first[0][arr.length-1],first[0][arr.length-1]);
    }

2.3 win3思路

win3的思路是借鉴win1 是基于win1来说的,win1中需要计算first和second的两个值。如果这样想 只计算一个值value,总数sum-value 只需要比较两者最大值就可以了。

 	public static int win3(int [] arr){
        if (arr == null || arr.length == 0){
            return  0;
        }
        int sum = 0;
        for (int i = 0; i < arr.length ; i++) {
            sum += arr[i];
        }
        int socore = p(arr,0,arr.length-1);
        return Math.max(sum-socore,socore);
    }

    public static int p(int [] arr,int i,int j){
        if (i == j){
            return arr[i];
        }
        if (i + 1 == j){
            return Math.max(arr[i],arr[j]);
        }
        return Math.max(arr[i]+Math.min(p(arr,i+2,j),p(arr,i+1,j-1)),
                arr[j]+Math.min(p(arr,i+1,j-1),p(arr,i,-2)));
    }

2.4 code

	package com.ncst.offer.ch1;

	import java.util.ArrayList;

	/**
	 * @author i
	 * @create 2020/7/1 10:20
	 * @Description
	 */
	public class Problem_03_CardsInLine {

    //1.针对谁先拿必然是先拿最大者,而后者就是拿最小者。依序就是 first->second->first—>second .....
    // a.其中 当i == j 对于first来说 可以拿到arr[i]
    // b.i == j 对于second来说 拿不到arr[i] 因为first具有优先权。
    public static int win1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        return Math.max(first(arr, 0, arr.length - 1), second(arr, 0, arr.length - 1));
    }

    public static int first(int[] arr, int i, int j) {
        if (i == j) {
            return arr[i];
        }
        return Math.max(arr[i] + second(arr, i + 1, j), arr[j] + second(arr, i, j - 1));
    }

    public static int second(int[] arr, int i, int j) {
        if (i == j) {
            return 0;
        }
        return Math.min(arr[i] + first(arr, i + 1, j), arr[j] + first(arr, i, j - 1));
    }

    public static int win2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        //初始化状态  dp方程
        int[][] first = new int[arr.length][arr.length];
        int[][] second = new int[arr.length][arr.length];

        for (int j = 0; j < arr.length; j++) {
            //初始化参数
            first[j][j] = arr[j];
            for (int i = j-1; i >=0 ; i--) {
                //进行决策
                first[i][j] = Math.max(arr[i]+second[i+1][j],arr[j]+second[i][j-1]);
                second[i][j] = Math.max(first[i+1][j],first[i][j-1]);
            }
        }
        return Math.max(first[0][arr.length-1],first[0][arr.length-1]);
    }


    public static int win3(int [] arr){
        if (arr == null || arr.length == 0){
            return  0;
        }
        int sum = 0;
        for (int i = 0; i < arr.length ; i++) {
            sum += arr[i];
        }
        int socore = p(arr,0,arr.length-1);
        return Math.max(sum-socore,socore);
    }

    public static int p(int [] arr,int i,int j){
        if (i == j){
            return arr[i];
        }
        if (i + 1 == j){
            return Math.max(arr[i],arr[j]);
        }
        return Math.max(arr[i]+Math.min(p(arr,i+2,j),p(arr,i+1,j-1)),
                arr[j]+Math.min(p(arr,i+1,j-1),p(arr,i,-2)));
    }


    public static int[] gengerateRandomArray() {
        int[] res = new int[(int) Math.random() * 20 + 1];
        for (int i = 0; i < res.length; i++) {
            res[i] = (int) Math.random() * 20 + 1;
        }
        return res;
    }

    public static void main(String[] args) {
        int timeNum = 500;
        boolean flag = false;
        for (int i = 0; i < timeNum; i++) {
            int[] res = gengerateRandomArray();
            int res1 = win1(res);
            int res2 = win2(res);
            int res3 = win3(res);
            if (res1 != res2 && res2 == res3) {
                flag = true;
            }
        }

        if (flag) {
            System.out.println("233333333333");
        } else {
            System.out.println("666666666666");
        }
    }

}