数据结构和算法经典100题-第22题

题目要求:
排成一条线的纸牌博弈问题
给定一个整形数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明,请返回最后获胜者的分数。


解题分析:
假设数组int arr[n],设函数f(i,j)表示数列a[i…j]序列被“先手拿”最终得分;假设g(i,j)表示数列a[i…j]序列“后手拿”最终得分。
分析可知f(i,j) = max{g(i+1,j)+arr[i], g(i,j-1)+arr[j]};
g(i,j) = min{f(i+1,j), f(i,j-1)}; (说明:因为双方都绝顶聪明,所以假设A先手,B后手,A一定把两种情况中小的留给后手选手)


在分析完题目后,我们发现这是一个递归问题,可以暴力递归直接解这个问题,代码如下:


class Game {
public:
    Game(std::vector<int> &ivecArray) : m_ivecArr(ivecArray) {};
    ~Game() {};
    int getScore();
    int f(int i, int j);
    int g(int i, int j);
private:
    std::vector<int> &m_ivecArr;
};

int Game::f(int i, int j) {
    if (i == j) {
        return m_ivecArr[i];
    }

    return max<int>( (m_ivecArr[i] + g(i+1, j)), (m_ivecArr[j] + g(i, j-1)) );
}

int Game::g(int i, int j) {
    if (i == j) {
        return 0;
    }

    return min<int>( f(i+1, j),  g(i, j-1) );
}

int Game::getScore() {
    if (ivecArr.empty()) {
        return 0;
    }

    return max<int>(f(0,(ivecArr.size()-1)) , g(0,ivecArr.size()-1));
}

然后我们分析一下时间复杂度和空间复杂度。
——————-假设数组长度为N——————————
时间复杂度:O(2ˆN)——因为每个递归的递推式都是:左1=右2
空间复杂度:O(N);
于是想到了动态规划打表,把重复的计算存起来,代码如下:

int getScore(std::vector<int> &ivecArr) {
    if (ivecArr.empty()) {
        return 0;
    }

    // 空间复杂度O(Nˆ2)
    int length = (int)(ivecArr.size() -1);
    int fScore[length+1][length+1];
    int gScore[length+1][length+1];

    for (int i = 0; i <= length; i++) {
        for (int j = 0; j <=length; j++) {
            fScore[i][j] = 0;
            gScore[i][j] = 0;
        }
    }

    // 时间复杂度O(Nˆ2)
    for (int j = 0; j <= length; ++j) {
        fScore[j][j] = ivecArr[j];

        for (int i = j-1; i >= 0; i--) {
            fScore[i][j] = max<int>((gScore[i+1][j] + ivecArr[i]), (gScore[i][j-1] + ivecArr[j]));
            gScore[i][j] = min<int>(fScore[i+1][j] , fScore[i][j-1]);
        }
    }

    return max<int>(fScore[0][length],gScore[0][length]);
}

int main(void) {
    int iarray[] = {1,100,2};
    size_t size = sizeof(iarray) / sizeof(int);
    vector<int> ivecArray(iarray,iarray+size);

    cout<<"Test case 1 :socre = "<<getScore(ivecArray)<<endl;
}

此方法的时间复杂度O(Nˆ2)—两次遍历;
空间复杂度O(Nˆ2)—打表占的空间;


OKay,完整源码附在下面:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

#ifdef _METHOLD_1_

class Game {
public:
    Game(std::vector<int> &ivecArray) : m_ivecArr(ivecArray) {};
    ~Game() {};
    int getScore();
    int f(int i, int j);
    int g(int i, int j);
private:
    std::vector<int> &m_ivecArr;
};

int Game::f(int i, int j) {
    if (i == j) {
        return m_ivecArr[i];
    }

    return max<int>( (m_ivecArr[i] + g(i+1, j)), (m_ivecArr[j] + g(i, j-1)) );
}

int Game::g(int i, int j) {
    if (i == j) {
        return 0;
    }

    return min<int>( f(i+1, j),  g(i, j-1) );
}

int Game::getScore() {
    if (ivecArr.empty()) {
        return 0;
    }

    return max<int>(f(0,(ivecArr.size()-1)) , g(0,ivecArr.size()-1));
}

#else

int getScore(std::vector<int> &ivecArr) {
    if (ivecArr.empty()) {
        return 0;
    }

    int length = (int)(ivecArr.size() -1);
    int fScore[length+1][length+1];
    int gScore[length+1][length+1];

    for (int i = 0; i <= length; i++) {
        for (int j = 0; j <=length; j++) {
            fScore[i][j] = 0;
            gScore[i][j] = 0;
        }
    }

    for (int j = 0; j <= length; ++j) {
        fScore[j][j] = ivecArr[j];

        for (int i = j-1; i >= 0; i--) {
            fScore[i][j] = max<int>((gScore[i+1][j] + ivecArr[i]), (gScore[i][j-1] + ivecArr[j]));
            gScore[i][j] = min<int>(fScore[i+1][j] , fScore[i][j-1]);
        }
    }

    return max<int>(fScore[0][length],gScore[0][length]);
}

#endif
int main(void) {
    int iarray[] = {1,100,2};
    size_t size = sizeof(iarray) / sizeof(int);
    vector<int> ivecArray(iarray,iarray+size);

    cout<<"Test case 1 :socre = "<<getScore(ivecArray)<<endl;
}

路漫漫其修远兮,吾将上下…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值