轮流挑选端点,试图获得最大值

时间:2014-03-06 07:57:50

标签: java dynamic-programming

假设有一个整数数组。您轮流与另一个人选择数组的端点(第一个或最后一个元素)。目标是实现一种算法来找到可能的最大分数(始终是第一个行动)。

示例:

int[] arr = {3, 0, 4, 11, 1};

pick 3
arr = {0, 4, 11, 1}

opponent picks 0 
arr = {4, 11, 1}

pick 4
arr = {11, 1}

opponent picks 1
arr = {11}

pick 11
arr = {}

maxPossible = 3 + 4 + 11 = 18

我得到了一个递归解决方案,但效率非常低。

我想找到动态编程解决方案(我是新手)。

有什么想法吗?

这是我天真的递归实现。我相信它是O(2 ^ N)

public int findBest(List<Integer> a) {
       return findBestHelper(a, 0, true);
}

public int findBestHelper(List<Integer> a, int score, boolean myTurn) {

    if(a.size() <= 0)
        return score;

    List<Integer> removeFirst = new ArrayList<Integer>(a);
    removeFirst.remove(0);

    List<Integer> removeLast = new ArrayList<Integer>(a);
    removeLast.remove(removeLast.size() - 1);

    if(myTurn)
        return Math.max(findBestHelper(removeFirst, score + a.get(0), false), findBestHelper(removeLast, score + a.get(a.size() - 1), false));
    else
        return Math.max(findBestHelper(removeFirst, score, true), findBestHelper(removeLast, score, true));

}

4 个答案:

答案 0 :(得分:0)

试用你的例子。

public void test()
    {
        int[] arr = {3, 0, 4, 11, 1};
        int sum = 0;
        int c1, c2, rs;
        for(int i=0;i< arr.length/2; i++)
        {
         c1 =  arr[i]  ;
         c2 = arr[arr.length - i -1];
         System.out.println("c1="+c1 + " - c2="+c2);
           sum += (c1>c2) ?c1:c2;

        }
        if(arr.length%2 >0)
        {
         System.out.println("center="+arr[arr.length/2]);
            sum += arr[arr.length/2];
        }
        System.out.println("sum="+sum );
    }

答案 1 :(得分:0)

我不确定这是不是最佳,但我真的想尝试一下:D

/**
 * @param array
 * @return
 */
public static int getMax(int[] array) {
    if (array.length == 0) {
        return 0;
    }

    int lastIndex = array.length - 1;
    boolean firstLarger = array[0] > array[lastIndex];
    return (firstLarger ? array[0] : array[lastIndex]) 
        + getMax(removeMin(Arrays.copyOfRange(array, firstLarger ? 1 : 0, firstLarger ? lastIndex + 1
        : lastIndex)));
}

/**
 * @param array
 * @return
 */
public static int[] removeMin(int[] array) {
    if (array.length == 0) {
        return array;
    }
    int lastIndex = array.length - 1;
    boolean firstLarger = array[0] > array[lastIndex];
    return Arrays.copyOfRange(array, firstLarger ? 0 : 1, firstLarger ? lastIndex : lastIndex + 1);
}

答案 2 :(得分:0)

动态编程表明我们需要一些问题的子集 简单的解决方案和扩展具有更多数据的子集解决方案的方法。

我不完全确定这是如何完成的,但这里有一个猜测 你需要进入哪个方向。

琐碎的情况是我们只剩下一个数组元素。得分了 最佳解决方案是该元素的价值。

如果我们有长度为n的数组的所有解决方案,我们可以得到最优 通过比较(数组的开始 - 最优解)来解决n + 1长度数组 没有start元素的n长度数组)(数组的结尾 - 最优解 没有结束元素的n长度数组)

类似于:

elems   2            3           4           5
3       B(3-0)3      E(4-3)1     E(11-1)10   B(3-6)-3
0       E(4-0)4      E(11-4)7    B(0- -6)6
4       E(11-4)7     B(4-10)-6
11      B(11-1)10
1       

第一列是只有一个元素的时候。第二列是2长度数组等。 每一行都是从该索引开始的数组。因此,我们通过比较(3 - 第一列第二行的最佳解)与(0 - )来获得第一行和第二列中的单元格。 拳头第一排的最优解。)

我很可能在写下决定和得分时犯了一两个错误,但是 我认为一般的想法可能有一些优点。

-edit -

艾哈迈德·Y·萨利赫的解决方案看起来很贪婪。我认为这不会为您提供最佳解决方案。

答案 3 :(得分:0)

递归解决方案的问题在于它多次计算多个状态序列。想象一下从列表开始

[10, 48, 29, 47, 15, 3, 41, 11, 19, 4]

现在想象两种可能的动作序列。以下行包含

  1. 当前状态列表
  2. 两位球员的得分
  3. 有效播放的索引(0或1)
  4. 第一个序列的开头:

    List [10, 48, 29, 47, 15, 3, 41, 11, 19, 4], scores [0, 0], active 0
    List [10, 48, 29, 47, 15, 3, 41, 11, 19], scores [4, 0], active 1
    List [10, 48, 29, 47, 15, 3, 41, 11], scores [4, 19], active 0
    List [48, 29, 47, 15, 3, 41, 11], scores [14, 19], active 1
    List [29, 47, 15, 3, 41, 11], scores [14, 67], active 0
    ...
    

    第二个序列的开始:

    List [10, 48, 29, 47, 15, 3, 41, 11, 19, 4], scores [0, 0], active 0
    List [48, 29, 47, 15, 3, 41, 11, 19, 4], scores [10, 0], active 1
    List [29, 47, 15, 3, 41, 11, 19, 4], scores [10, 48], active 0
    List [29, 47, 15, 3, 41, 11, 19], scores [14, 48], active 1
    List [29, 47, 15, 3, 41, 11], scores [14, 67], active 0
    ...
    

    此时,两个序列都达到了相同的状态!基本上,简单直观的总结是序列

    Player 0 takes right
    Player 1 takes right
    Player 0 takes left 
    Player 1 takes left
    

    Player 0 takes left
    Player 1 takes left
    Player 0 takes right
    Player 1 takes right
    

    导致相同的状态。在递归解决方案中,计算继续进行,结果状态两次,并且稍后发生的重复状态再次处理两次 - 并且此“树”增长得相当快。

    因此,通过某种动态编程解决此问题的一种方法如下:从某个游戏状态开始,可以计算所有可能的后继者,并将它们存储在一个集合中。然后计算这些状态的后继者,跟踪到目前为止所取得的分数。

    这是此方法的一种实现,它还会打印已跳过的结果:

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.LinkedHashMap;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Random;
    import java.util.Set;
    
    public class PickingGame
    {
        private static final boolean PRINT_SKIPPED_STATES = true;
    
        public static void main(String[] args)
        {
            //solve(Arrays.asList(3, 0, 4, 11, 1));
            solve(createLargerList());
        }
    
        private static List<Integer> createLargerList()
        {
            int max = 50;
            int n = 10;
            Random random = new Random(0);
            List<Integer> list = new ArrayList<Integer>();
            for (int i=0; i<n; i++)
            {
                list.add(random.nextInt(max));
            }
            return list;
        }
    
        private static void solve(List<Integer> list)
        {
            System.out.println("Starting with "+list);
    
            Set<PickingGameState> currentStates = 
                new LinkedHashSet<PickingGameState>();
            currentStates.add(new PickingGameState(list));
            Set<PickingGameState> finalStates = 
                new LinkedHashSet<PickingGameState>();
            while (!currentStates.isEmpty())
            {
                PickingGameState currentState = currentStates.iterator().next();
                currentStates.remove(currentState);
                if (currentState.list.size() == 0)
                {
                    finalStates.add(currentState);
                }
                else 
                {
                    PickingGameState next0 = new PickingGameState(currentState, true);
                    boolean changed0 = currentStates.add(next0);
                    if (PRINT_SKIPPED_STATES && !changed0)
                    {
                        printDebugInfo(currentStates, next0);
                    }
    
                    if (currentState.list.size() > 1)
                    {
                        PickingGameState next1 = new PickingGameState(currentState, false);
                        boolean changed1 = currentStates.add(next1);
                        if (PRINT_SKIPPED_STATES && !changed1)
                        {
                            printDebugInfo(currentStates, next0);
                        }
                    }
    
                }
            }
    
            int maxScore = 0;
            PickingGameState maxPickingGameState = null;
            for (PickingGameState p : finalStates)
            {
                if (p.scores[0] > maxScore)
                {
                    maxScore = p.scores[0];
                    maxPickingGameState = p;
                }
                if (p.scores[1] > maxScore)
                {
                    maxScore = p.scores[1];
                    maxPickingGameState = p;
                }
            }
    
            System.out.println("The best result that can be achieved is");
            print(maxPickingGameState);
        }
    
        // Print all predecessors of the given state,
        // and finally the state itself
        private static void print(PickingGameState p)
        {
            if (p.predecessor == null)
            {
                System.out.println(p);
            }
            else
            {
                print(p.predecessor);
                System.out.println(p);
            }
        }
    
        // Print details about the given state that was ignored
        // because an "equal" state was already in the given
        // sequence
        private static void printDebugInfo(
            Iterable<PickingGameState> iterable, PickingGameState newState)
        {
            System.out.println("Ignoring "+newState);
            System.out.println("It was achieved with this sequence of moves:");
            print(newState);
            System.out.println("But the same state was already achieved with");
            print(findEqual(iterable, newState));
            System.out.println("");
        }
    
        // Returns the first element in the given sequence
        // that is equal to the given element
        private static <T> T findEqual(Iterable<T> iterable, T element)
        {
            for (T e : iterable)
            {
                if (e.equals(element))
                {
                    return e;
                }
            }
            return null;
        }
    
    }
    
    
    class PickingGameState
    {
        PickingGameState predecessor;
        List<Integer> list;
        int scores[];
        int active;
    
        PickingGameState(List<Integer> list)
        {
            this.predecessor = null;
            this.list = new ArrayList<Integer>(list);
            this.scores = new int[2]; 
            this.active = 0;
        }
        PickingGameState(PickingGameState other, boolean removeFirst)
        {
            this.predecessor = other;
            this.list = new ArrayList<Integer>(other.list);
            this.scores = other.scores.clone();
            if (removeFirst)
            {
                this.scores[other.active] += this.list.remove(0);
            }
            else
            {
                this.scores[other.active] += this.list.remove(this.list.size()-1);
            }
            this.active = 1-other.active;
        }
    
        @Override
        public String toString()
        {
            return "List "+list+", scores "+Arrays.toString(scores)+", active "+active;
        }
    
        @Override
        public int hashCode()
        {
            final int prime = 31;
            int result = 1;
            result = prime * result + active;
            result = prime * result + ((list == null) ? 0 : list.hashCode());
            result = prime * result + Arrays.hashCode(scores);
            return result;
        }
        @Override
        public boolean equals(Object obj)
        {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            PickingGameState other = (PickingGameState) obj;
            if (active != other.active)
                return false;
            if (list == null)
            {
                if (other.list != null)
                    return false;
            }
            else if (!list.equals(other.list))
                return false;
            if (!Arrays.equals(scores, other.scores))
                return false;
            return true;
        }
    
    }