NCPC 2016 D - Daydreaming Stockbroker&&G - Game Rank

这次比赛打的自闭了,D题和G题做了三四个小时没A,这两个题都是模拟题,但是总是在模拟过程中出一些问题(细节处理不好),这道D题也是模拟题目,但是赛后搜了一下题解,有大佬都不用模拟过程,直接计算,我还是太菜了,你大佬还是你大佬orz。

下面粘一下题目


题目大意是你刚开始有100本金,然后给出n天的股票价格(可以以这个价格买入,也可以以这个价格卖出),但是你手上的股票最多有10w股,求你最多能挣多少钱。

下面说一下大佬代码的思想:都不用开数组来保存每一天股票的价格,直接一边读入一遍处理,即每天都处理当天的股票。核心思想是用一个flag标记一下你买入(或者卖出)股票的价格,如果第二天的价格比flag要高的话就在第二天补上这两天的股票差价。如果第二天的价格比flag要低的话就在第二天买入这个股票。点睛之笔在于这个代码并不是用num+=的形式来记录股票的数量,而是直接用num=当前手里的钱/当天股票的价格,同时sum(手里的钱数)并不会因为买入股票而减少(sum一直是以补差价的形式来加的,即sum+=的形式),这样一来如果以后几天有更便宜的股票你也可以直接购买,这样前一次的num(股票的数目)并不会影响到后面,例如你现在手里有200本金,第一天股票的价格为50,第二天股票的价格为40,第一天你买入的num=200/50=4,同时flag=50(即记录当前买入股票时的价格),但是这并不是最优的方法,因为第二天股票价格更低,所以我们应该在第二天买入,当你输入第二天的股票价格时,由于(40<flag),所以这时候num=200/40=5,这时才是最优方案。

上面说的是买入的思想,其实卖出的思想也是一样,如果flag<x(x代表当天股票价格),就说明现在股票售价比我们买入的时候要高,我们是有利可图的,这时候我们就卖出,同时sum+=num*(x-flag)//这里是以差价的形式增加的,非常奇妙的思想! 例如:第一天股票价格是100,第二天是101,第三天是300,我们一眼可以看出应该在第一天买入,第三天卖出这样可以利益最大化,下面我们模拟一下通过大佬代码的过程,首先我们在第一天买入(价格为100)同时flag=100,第二天价格为101>100,所以我们“卖出”(注意!!!这里的卖出是“伪卖出”,即并不是真正的卖出),同时flag标记为101,这时候我们的sum是101,即原本的100+差价1块。到了第三天,股票价格为300,比第二天的101要大,300>flag,所以sum+=(300-101)*1,300-101是差价,后面的那个*1是有一支股票,所以sum其实是一直以补差价的形式增加的,这也是代码的巧妙之处!!!

最后还有一个重点就是num(股票的数目)的值要用long long,不用long long 会WA,虽然题目说你手上最多有10w股,但是你用sum/x之后会超过int的范围,所以用long long 记录

下面是代码:

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int i,n,x,flag=100;
    long long int sum=100,num=0;
    scanf("%d",&n);
    for(i=0; i<n; i++)
    {
        scanf("%d",&x);//x代表当天股票的价格
        if(x<=flag)
        {
            num=sum/x;//num用long long 否则会WA
            flag=x;
            num=min(num,(long long int)100000);//股票最多10w股,不能超过10w股
        }
        else
        {
            sum+=num*(x-flag);//补差价
            flag=x;
        }
    }
    printf("%lld\n",sum);
    return 0;
}

虽然代码很短 但是思想是真的强,我看着代码理解了好一会才理解过来。

--------------------------------分界线----------------------------------

接下来是G题,G题怎么说呢,也是模拟题,但是因为题目是英文,我们队的英语水平不高所以只能读懂大概,很多细节根本不明白,这导致了我们在不完全理解题意的状态下敲了三四个小时没敲出来,惭愧- -!

先粘一下题目
题目大意:就是炉石的排位机制……

共25个等级和传说等级,赢一局一颗星,连续赢三局以上额外多加一颗星(高于5级后不多加星),满星后再多一颗星才能升等级,并且升级后新等级有一颗星。

输一局减一颗星(21-25级输了不扣星,但是仍会断连胜),输了后没星仍可以保持段位,但是没星后又掉星了会掉等级。传说后不论输赢均不掉段。

各等级的星星为

• Rank 25-21: 2 stars

• Rank 20-16: 3 stars

• Rank 15-11: 4 stars

• Rank 10-1: 5 stars

这个题就是纯粹的模拟了,没有像D题那样更”简单“的代码了,我们组问题主要出在了段位下降一次之后星也会少一颗,比如现在是Rank2 0颗星,如果输一次的话就变成了Rank3 1颗星(满星为2颗),我们一直以为是变成Rank3 2颗星,还有一个问题就是段位保护那里边界问题,题目描述是Rank21-25处于段为保护 输了不会掉星,所以我们只考虑了处于段位21-25输的时候不掉星,没考虑到如果是Rank20 0颗星的话会怎么样,实际上如果是Rank20 0颗星同时输掉一次的话应该还是Rank20 0颗星,我们之前没考虑到这个样例,所以根据我们的程序会变成Rank21 2颗星(满星为2颗),所以我们一直在WA并且找不出错误- -

下面是代码:

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int a[200]= {0,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,2,2,2,2,2};//数组a记录每个段位满星的个数,顺序是逆序的,为了和ranks同步
    int ranks=25,starts=0,times=0,i,len;
    char str[10010];
    scanf("%s",str);
    len=strlen(str);
    for(i=0; i<len; i++)
    {
        if(ranks==0)//如果是Rank0的话就说明达到最顶峰了,直接break出去输出Legend就行了
            break;
        if(str[i]=='W')
        {
            times++;//连胜+1
            starts++;//星数+1
            if(times>=3&&ranks>=6&&ranks<=25)//如果连胜>3同时段位没有在5之上的话就触发连胜额外加的那一颗星
                starts++;
            if(starts>a[ranks])//如果星数>该段位最多星数,即代表晋级段位
            {
                starts-=a[ranks];
                ranks--;//段位上升一个
            }
        }
        else
        {
            times=0;//终结连胜
            if(ranks>=21&&ranks<=25)//处于段位保护状态,输了也没什么操作,所以直接contin
                continue;
            starts--;//星数-1
            if(starts<0)//如果星数为负
            {
                if(ranks==20)//特殊情况,我们队没有想到的情况
                    starts=0;
                else
                {
                    ranks++;
                    starts=a[ranks]-1;//掉段之后星数也要再-1
                }
            }
        }
    }
    if(ranks)
        printf("%d\n",ranks);
    else
        printf("Legend\n");
    return 0;
}

总体来说这次比赛做的十分不好,原因主要在于英语不好题意没读太明白(G题),对于一些边界值模棱两可,找不出错误。还有思维上确实还有待提高(D题)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值