寻找动态编程解决方案

时间:2014-08-19 16:47:29

标签: dynamic-programming

问题: 有一个由N块砖组成的堆栈。你和你的朋友决定使用这个堆栈玩游戏。在这个游戏中,人们可以从顶部移除1/2/3砖块,并将玩家移除的砖块上的数字添加到他的分数中。你必须以这样一种方式进行游戏,即你获得最大可能的分数,同时你的朋友也会以最佳方式进行游戏,并且你是第一步。

输入格式 第一行将包含整数T,即测试用例的数量。对应于每个测试用例将有两行,第一行将包含一个数字N,即堆栈中的元素数量,下一行将包含N个数字,即从上到下写在砖块上的数字。

输出格式 对于每个测试用例,打印包含最高分数的单行。 我尝试了递归但没有工作

int recurse(int length, int sequence[5], int i) {
    if(length - i < 3) {
       int sum = 0;
       for(i; i < length; i++) sum += sequence[i];
       return sum;
    } else {
        int sum1 = 0;
        int sum2 = 0;
        int sum3 = 0;
        sum1 += recurse(length, sequence, i+1);
        sum2 += recurse(length, sequence, i+2);
        sum3 += recurse(length, sequence, i+3);
        return max(max(sum1,sum2),sum3);
    }
}



int main() {
    int sequence[] = {0, 0, 9, 1, 999};
    int length = 5;
    cout << recurse(length, sequence, 0);
    return 0;
}

4 个答案:

答案 0 :(得分:2)

我解决这个问题的方法如下:

两位选手都表现最佳。

因此,解决方案的构建方式不需要考虑玩家。这是因为对于任何给定状态的砖块,两位球员都会选择最适合他们的选择。

基本案例:

任何一个玩家,当留下最后一个/两个/三个砖块时,将选择移除所有砖块。

为了方便起见,我们假设数组实际上是以相反的顺序排列(即[0]是堆栈中最底部砖块的值)(这可以通过执行来轻松实现)对阵列进行反向操作。)

所以,基本案例是:

bclass

构建最终解决方案:

现在,在每次迭代中,玩家有3个选择。

  1. 挑砖(i),或,
  2. 挑砖(i和i-1),或
  3. 挑砖(i,i-1和i-2)
  4. 如果玩家选择了选项1,则会产生以下结果:

    • 玩家从砖块(i)(+ a [i])
    • 获取[i]点
    • 将无法获得对手移除的砖块上的点数。该值存储在dp [i-1]中(对手最终会根据玩家的选择进行评分)。
    • 肯定会在砖块上获得未被对手移除的点数。 (+所有砖块的总和直至砖块(i-1)未被对手移除)

    用于存储砖块的部分和的前缀数组可以如下计算:

    # Base Cases
    dp[0] = a[0]
    dp[1] = a[0]+a[1]
    dp[2] = a[0]+a[1]+a[2]
    

    而且,现在,如果玩家选择了选择1,则得分为:

    # build prefix sum array
    pre = [a[0]]
    for i in range(1,n):
        pre.append(pre[-1]+a[i])
    

    同样,对于选择2和3.因此,我们得到:

    ans1 = a[i] + (pre[i-1] - dp[i-1]) 
    

    现在,每位玩家都希望最大化此值。因此,在每次迭代中,我们选择ans1,ans2和ans3中的最大值。     dp [i] = max(ans1,ans2,ans3)

    现在,我们所要做的就是从3迭代到n-1以获得所需的解决方案。

    以下是python中的最终片段:

    ans1 = a[i]+ (pre[i-1] - dp[i-1])   # if we pick only ith brick
    ans2 = a[i]+a[i-1]+(pre[i-2] - dp[i-2]) # pick 2 bricks
    ans3 = a[i]+a[i-1]+a[i-2]+(pre[i-3] - dp[i-3])    # pick 3 bricks
    

答案 1 :(得分:1)

乍一看,由于以下几个原因,您的代码似乎完全错误:

  1. 不考虑玩家。你拿砖头或你的朋友拿砖不一样(你最大化你的得分,总数当然总是砖块上的得分总和)。

  2. 看起来只是某种形式的递归,没有任何记忆,这种方法显然会爆炸到指数计算时间(你正在使用&#34;暴力&#34;方法,枚举所有可能的游戏)。

  3. 动态编程方法显然是可行的,因为游戏的最佳延续并不取决于你是如何达到某个状态的。对于你需要的游戏状态

    • 旁边的人(你或你的朋友)
    • 堆叠上剩余多少块砖

    通过这两个输入,您可以计算从该点到游戏结束时可以收集多少。要做到这一点有两种情况

    1。轮到你了

    你需要尝试收集1,2或3并在下一个对手必须选择的游戏状态下递归调用。在这三种情况中,你保留了最高的结果

    2。它的对手转身

    您需要模拟1,2或3块砖的集合,并在您必须选择的下一个游戏状态下递归调用。在这三种情况中你保持最低结果(因为对手试图最大化他/她的结果,而不是你的结果)。

    在函数的最开始,您只需要检查之前是否处理过相同的游戏状态,并且从计算返回时需要存储结果。由于这种查找/记忆,搜索时间不是指数级的,而是在不同游戏状态的数量上呈线性(只有2 * N,其中N是砖的数量)。

    在Python中:

    memory = {}
    bricks = [0, 0, 9, 1, 999]
    
    def maxResult(my_turn, index):
        key = (my_turn, index)
        if key in memory:
            return memory[key]
        if index == len(bricks):
            result = 0
        elif my_turn:
            result = None
            s = 0
            for i in range(index, min(index+3, len(bricks))):
                s += bricks[i]
                x = s + maxResult(False, i+1)
                if result is None or x > result:
                    result = x
        else:
            result = None
            for i in range(index, min(index+3, len(bricks))):
                x = maxResult(True, i+1)
                if result is None or x < result:
                    result = x
        memory[key] = result
        return result
    
    print maxResult(True, 0)
    

答案 2 :(得分:1)

import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class Solution {

        public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int noTest=sc.nextInt();
        for(int i=0; i<noTest; i++){
            int noBrick=sc.nextInt();
            ArrayList<Integer> arr=new ArrayList<Integer>();
            for (int j=0; j<noBrick; j++){
                arr.add(sc.nextInt());
            }
            long sum[]= new long[noBrick];
            sum[noBrick-1]= arr.get(noBrick-1); 
            for (int j=noBrick-2; j>=0; j--){
                sum[j]= sum[j+1]+ arr.get(j); 
            }
            long[] max=new long[noBrick];
            if(noBrick>=1)
            max[noBrick-1]=arr.get(noBrick-1);
            if(noBrick>=2)
            max[noBrick-2]=(int)Math.max(arr.get(noBrick-2),max[noBrick-1]+arr.get(noBrick-2));
            if(noBrick>=3)
            max[noBrick-3]=(int)Math.max(arr.get(noBrick-3),max[noBrick-2]+arr.get(noBrick-3));
            if(noBrick>=4){
                for (int j=noBrick-4; j>=0; j--){
                    long opt1= arr.get(j)+sum[j+1]-max[j+1];
                    long opt2= arr.get(j)+arr.get(j+1)+sum[j+2]-max[j+2];
                    long opt3= arr.get(j)+arr.get(j+1)+arr.get(j+2)+sum[j+3]-max[j+3];
                    max[j]= (long)Math.max(opt1,Math.max(opt2,opt3));
                }
            }
            long cost= max[0]; 
            System.out.println(cost);
        }

    }
}

我尝试使用Java,似乎工作正常。

答案 3 :(得分:0)

这是一个更好的解决方案,我在互联网上找到没有递归。

#include <iostream>
#include <fstream>
#include <algorithm>
#define MAXINDEX 10001
using namespace std;

long long maxResult(int a[MAXINDEX], int LENGTH){
    long long prefixSum [MAXINDEX] = {0};
    prefixSum[0] = a[0];
    for(int i = 1; i < LENGTH; i++){
        prefixSum[i] += prefixSum[i-1] + a[i];
    }

    long long dp[MAXINDEX] = {0};
    dp[0] = a[0];
    dp[1] = dp[0] + a[1];
    dp[2] = dp[1] + a[2];
    for(int k = 3; k < LENGTH; k++){
        long long x = prefixSum[k-1] + a[k] - dp[k-1];
        long long y = prefixSum[k-2] + a[k] + a[k-1] - dp[k-2];
        long long z = prefixSum[k-3] + a[k] + a[k-1] + a[k-2] - dp[k-3];
        dp[k] = max(x,max(y,z));
    }
    return dp[LENGTH-1];
}

using namespace std;

int main(){
    int cases;
    int bricks[MAXINDEX];
    ifstream fin("test.in");
    fin >> cases;
    for (int i = 0; i < cases; i++){
        long n;
        fin >> n;
        for(int j = 0; j < n; j++) fin >> bricks[j];
        reverse(bricks, bricks+n);
        cout << maxResult(bricks, n)<< endl;
    }
    return 0;
}