UVA 10891 Game of Sum(区间DP)

本文介绍了一种基于动态规划的博弈论问题解决方案,通过计算最大收益帮助玩家在游戏中获胜。详细解析了状态转移方程,并提供了两种实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Game of Sum

这里写图片描述

思路:
dp[i][j]表示在区间i~j内先手所能获得的最大利益;
ls[i][j]表示先手从左边拿完后,在剩下的区间i~j内,后手所能获得的最小利益;
rs[i][j]表示先手从右边拿完后,在剩下的区间i~j内,后手所能获得的最小利益;

则dp[i][j]=sum[i][j]-min(min(ls[i+1][j],rs[i][j-1]),0);
解释一下上述式子,先手拿共有三种情况:
1.从左边拿1个,则后手剩余ls[i+1][j]
2.从右边拿1个,则后手剩余rs[i][j-1]
3.直接拿完,后手剩余0

问:为什么只拿一个?
答:ls[i][j]=min(dp[i][j],ls[i+1][j]);
       rs[i][j]=min(dp[i][j],rs[i][j-1]);
一个只是形式上的一个,因为ls[i][j]和rs[i][j]在动态地更新最小值,也就是说
dp[i][j]=sum[i][j]-min(min(ls[i+1][j],rs[i][j-1]),0)得到的是在区间i~j内拿k(1<=k<=j-i)个的最大利益

最后答案ans=dp[1][n]-(sum[1][n]-dp[1][n])=2*dp[1][n]-sum[1][n](具体详见代码)

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

int dp[110][110],sum[110],ls[110][110],rs[110][110];
int n;

int main()
{
    int x;
    while(~scanf("%d",&n),n)
    {
        for(int i=1; i<=n; ++i)
        {
            scanf("%d",&x);
            sum[i]=sum[i-1]+x;
            dp[i][i]=ls[i][i]=rs[i][i]=x;
        }
        for(int l=1; l<n; ++l)
        {
            for(int i=1; i<=n-l; ++i)
            {
                int j=i+l;
                dp[i][j]=sum[j]-sum[i-1]-min(min(ls[i+1][j],rs[i][j-1]),0);
                ls[i][j]=min(dp[i][j],ls[i+1][j]);
                rs[i][j]=min(dp[i][j],rs[i][j-1]);
            }
        }
        printf("%d\n",2*dp[1][n]-sum[n]);
    }
    return 0;
}



看不懂?没关系,下面有个简单的

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

int dp[110][110],sum[110];
int n;

int main()
{
    int x;
    while(~scanf("%d",&n),n)
    {
        for(int i=0; i<=n; ++i)
            for(int j=0; j<=n; ++j)
                dp[i][j]=-999999999;
        for(int i=1; i<=n; ++i)
        {
            scanf("%d",&x);
            sum[i]=sum[i-1]+x;
            dp[i][i]=x;
        }
        for(int l=1; l<n; ++l) //区间间隔
            for(int i=1; i+l<=n; ++i) //起点
            {
                int j=i+l;//终点
                for(int k=i; k<j; ++k) //先手拿1~n-1的情况
                    dp[i][j]=max(dp[i][j],sum[j]-sum[i-1]-min(dp[i][k],dp[k+1][j]));//从左边拿,从右边拿
                dp[i][j]=max(dp[i][j],sum[j]-sum[i-1]);//先手拿完的情况
            }
        printf("%d\n",2*dp[1][n]-sum[n]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值