假设有一个整数数组。您轮流与另一个人选择数组的端点(第一个或最后一个元素)。目标是实现一种算法来找到可能的最大分数(始终是第一个行动)。
示例:
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));
}
答案 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]
现在想象两种可能的动作序列。以下行包含
第一个序列的开头:
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;
}
}