uva 10891 Game of Sum

原题:
This is a two player game. Initially there are n integer numbers in an array and players A and B get
chance to take them alternatively. Each player can take one or more numbers from the left or right end
of the array but cannot take from both ends at a time. He can take as many consecutive numbers as he
wants during his time. The game ends when all numbers are taken from the array by the players. The
point of each player is calculated by the summation of the numbers, which he has taken. Each player
tries to achieve more points from other. If both players play optimally and player A starts the game
then how much more point can player A get than player B?
Input
The input consists of a number of cases. Each case starts with a line specifying the integer n (0 <
n ≤ 100), the number of elements in the array. After that, n numbers are given for the game. Input is
terminated by a line where n = 0.
Output
For each test case, print a number, which represents the maximum difference that the first player
obtained after playing this game optimally.
Sample Input
4
4 -10 -20 7
4
1 2 3 4
0
Sample Output
7
10
大意:
给你一堆数,然后又A和B两个人做一个游戏。游戏规则是两个人轮流取数,每次可以从任意一头取数,而且每次可以拿多个连续的数,但是不能两头拿。如果A先选,而且两人都用最优策略,问最后先手和后手的差是多少?

#include <bits/stdc++.h>
using namespace std;
//fstream in,out;
int n,sum[101];
int d[101][101];
bool vis[101][101];
int dp(int i,int j)
{
    if(vis[i][j])
        return d[i][j];
    vis[i][j]=true;
    int tmp=0;
    for(int k=i+1;k<=j;k++)
        tmp=min(tmp,dp(k,j));
    for(int k=j-1;k>=i;k--)
        tmp=min(tmp,dp(i,k));
    d[i][j]=sum[j]-sum[i-1]-tmp;
    return d[i][j];
}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n,n)
    {
        memset(d,0,sizeof(d));
        memset(sum,0,sizeof(sum));
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            int t;
            cin>>t;
            sum[i]=sum[i-1]+t;
        }
        dp(1,n);
        cout<<d[1][n]*2-sum[n]<<endl;
    }
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
//fstream in,out;
int dp[101][101],sum[101],f[101][101],g[101][101],n;
int main()
{
    ios::sync_with_stdio(false);
    int i,j,l;
    while(cin>>n,n)
    {
        memset(dp,0,sizeof(dp));
        sum[0]=0;
        for(i=1;i<=n;i++)
        {
            int t;
            cin>>t;
            sum[i]=sum[i-1]+t;
            f[i][i]=g[i][i]=dp[i][i]=t;
        }
        for(l=1;l<n;l++)
        {
            for(i=1;i+l<=n;i++)
            {
                j=i+l;
                int tmp=0;
                tmp=min(tmp,f[i+1][j]);
                tmp=min(tmp,g[i][j-1]);
                dp[i][j]=sum[j]-sum[i-1]-tmp;
                f[i][j]=min(dp[i][j],f[i+1][j]);
                g[i][j]=min(dp[i][j],g[i][j-1]);
            }
        }
        cout<<dp[1][n]*2-sum[n]<<endl;
    }
    return 0;
}

解答:
此题目参考博客
http://www.lxway.com/4001256026.htm(推荐)
http://www.tuicool.com/articles/fEjAzu

此题没想出来,后来看别人的博客明白了。不过也有一些思维成果,刚拿到题目的时候并没有想出状态量有什么,所以先手动模拟模拟一下,看看是否能有点启发。
1.对于一个博弈类游戏,每个人都采用最优策略,那么两个人的行为和决策是一模一样的,那么转移方程在建立的时候就不需要考虑两个人分别该怎么办。
2.对于一个给出的一堆数,决策过程也是很简单的,那就是拿左边还是拿右边?而且拿多少的问题。
我分析到2的情况时想出了状态可以用dp[i][j]表示为在取i到j个数时得到的最优过程。
接下来想怎样建立状态转移,和如果断定先手后手的问题,不过可惜没想出来。
之前已经分析了两个人都是用最优策略,那么先手后手的问题可以随意定义一个就好。看别人的博客都是定义先手作为状态。那么状态时怎样转移的呢,这里有一个在博弈上的问题叫极大极小算法。这个算法借用上面参考的一篇博客总结出的一句话就是:总和是一定的,所以一个人的得分越高,另一个人的得分越低。所以我们可以分析,另一个人在所能取得所有最优选择中得分最低的分数 m ,sum-m即为我的最高得分。
到这里明白了,每个人都用优策略,考虑dp[i][j]表示取i到j个数时的先手的最优策略,一定是dp[i][k]和dp[k][j](也就是dp[i][j]的子状态)这些之前判断的最优策略里面选出一个最烂的,然后用sum[i,j](i到j的和)减去,就是这个人的最优策略。
所以状态转移方程为dp[i][j]=sum[i][j]-min(dp[k][j],dp[i][k]) ,可以用记忆化搜索,也可以用递推。如果用递推来算,上面的参考博客里用空间交换时间复杂度,非常好的思路!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值