编程题—小萌的副本生涯

内容会持续更新,有错误的地方欢迎指正,谢谢!

题目

在主城站街很久之后,小萌决定不能就这样的浪费时间虚度青春,他打算去打副本。
这次的副本只有一个BOSS,而且BOSS是不需要击杀的,只需要和它比智力…….
BOSS会列出一正整数的序列,由小萌先开始,然后两个人轮流从序列的任意一端取数,取得的数累加到积分里,当所有数都取完,游戏结束。
假设小萌和BOSS都很聪明,两个人取数的方法都是最优策略,问最后两人得分各是多少。

输入
第一行:一个正整数N(2 ≤ N ≤ 100),表示序列中正整数的个数。
第二行至末尾:用空格隔开的N个正整数(1 ≤ a[i] ≤ 200)
输出
只有一行,用空格隔开的两个数,小萌的得分和BOSS的得分。

样例输入
6
4 7 2 9 5 2
样例输出
18 11

分析

送分题,贪婪思想,步步为赢即可。
玩家和BOSS有这么笨吗?肯定都是有远见的聪明人,所以,这道题其实考的是动态规划,这里我们用自顶向下的动态规划(即会记录重复子问题结果的改进版递归)来做。

还没搞清什么是动态规划和递归的小伙伴,请见:http://blog.csdn.net/billcyj/article/details/78765584

所用数据结构: 数组就好

代码

#include <iostream>
#include <algorithm>
using namespace std;

//res矩阵用于记录已求出的值,便于今后在遇到重复的(又可叫相同的)子问题时使用
int res[105][105];//ACM竞赛中,习惯性声明比 N的上限=100 大的空间,以防意外溢出
int a[105];//直接取比 N的上限 大的数,不用根据n为多少取多少,因为n是变量不是常量
int n;
//left为剩余字符串最左边那个元素的索引,right同理,isTurn用于控制该谁取数(比如剩奇数个时,该玩家取)
int CalSum(int left,int right,bool isTurn)
{    
    if(res[left][right])//如果已存在这个值,那直接使用,这是动态规划的核心思想
        return res[left][right];
    if(left==right)//如果只剩一个数了
    {
        if(!isTurn)//如果该玩家取数,直接取走并存入res,待其它递归使用
            return res[left][right]=a[left];
        else//如果该BOSS取数,那玩家啥也不取(即取0),但要把0存入res待用
            return res[left][right]=0;
    }  
    if(!isTurn)//如果该玩家取数,那就取从长远来看的max,而不是取当前的max咯~
        return res[left][right]=max(CalSum(left+1,right,!isTurn)+a[left],CalSum(left,right-1,!isTurn)+a[right]);
    else//如果该BOSS取数,那玩家不取数,那被BOSS取数后剩下的,肯定是从长远来看的min咯~
        return res[left][right]=min(CalSum(left+1,right,!isTurn),CalSum(left,right-1,!isTurn));
}

int main()
{
    cin>>n;
    int sum=0;
    for(int i=0;i<n;++i)
    {
        cin>>a[i];
        sum+=a[i];
    }
    int res=CalSum(0,n-1,false);//传入第一位的索引0和最后一位的索引n-1
    cout<<res<<" "<<sum-res<<endl;
    return 0;
}

其实,这道题的代码还能优化!

根据题目:N个正整数(1 ≤ a[i] ≤ 200)。大家试试画图+举例+分解这方法,如果有N=2n(偶数)个正整数,那么岂不是有一方永远取索引为奇数的那位,另一方永远取索引为偶数的那位?若索引为奇数的数之和大于索引为偶数的数之和,那聪明的玩家得分肯定是索引为奇数的数之和。当然,有N=2n+1个正整数的时候,还是按着上面的代码算。但是,我们如果这样做,对我们代码效率将是大大地优化!

优化后的main():

int main()
{
    cin>>n;
    int res,sum;
    for(int i=0;i<n;++i)
    {
        cin>>a[i];
        sum+=a[i];
    }

    int sum1=0,sum2=0;
    if(n%2==0)
    {
        for(int i=0;i<n;++i)
        {
            if(i%2==0)//n%2可换成(n&1),用位运算代替取余,由于优先级问题括号一定要加
                sum1+=a[i];//索引为偶数位的和
            else
                sum2+=a[i];//索引为奇数位的和
        }
        res=sum1>sum2?sum1:sum2;
    }
    else
        res=CalSum(0,n-1,false);//传入第一位的索引0和最后一位的索引n-1
    cout<<res<<" "<<sum-res<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值