ACM总结(第四、五周)

这两周主要学习内容是动态规划,但由于自己参加了比赛,所以说学到的东西还是挺多的。首先对于动态规划的题目由于自己前几天没有太多的去学习,以至于现在对动态规划的题目没有思路,还没有摸索到一些规律,所以这两天需要注重一下。其次对我印象最深的一类题是博弈题,这种题目特别有意思,总能通过一些题目发现有趣的规律。最后就是通过这几天的训练,对比赛的题目也有了初步了解。
一、动态规划
对于动态规划,由于自己还不熟练,只总结几个最经典的题目。
1、最长上升子序列
第一次看到这种题目时,自己首先想到的还是贪心,不过最后发现贪心没办法确定这个最长的子序列,因为它的开头与结尾都是不确定的,每个元素都可以是开头,同时每个元素也都可以时结尾,我们需要遍历每一个元素。这就需要我们分阶段去求最优值,这便需要用动态规划的思想。
解题思路:
我们可以这样想,求前i个数的最长上升子序列,就相当于求i个数之前的最长上升子序列,然后把第i个数与第再把每个子序列的最右端的数与第i个数进行比较,观察是否需要把第i个数也算上。

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

int main()
{
    int n;
    int x[10000];
    priority_queue<int>v;//这里使用优先队列找最大值,也可以sort排序
    int imaxlen[10000];//表示以i为“末点”的最长上升子序列
    cin>>n;
    for(int a=0;a<n;a++)
        cin>>x[a];
    imaxlen[0]=1;
    for(int a=1;a<n;a++)
    {
        int maxlen=0;
        for(int b=0;b<a;b++)
            if(x[a]>x[b])
        {
            if(maxlen<imaxlen[b])
                maxlen=imaxlen[b];
        }
        imaxlen[a]=maxlen+1;
    }
    for(int a=0;a<n;a++)
        v.push(imaxlen[a]);
        cout<<v.top();
    return 0;
}

2、最大连续子段和
这个问题与最大上升子序列有相似之处,我们也可以求以i结尾的最大字段和。可以判断前i-1个数的最大字段和与第i个数的和是否大于第i个数的值,如果大于第i个数的值,那么以i结尾的最大字段和为前i-1个数的最大字段和与第i个数的和,否则为第i个数的值。

dp[a]=max(dp[a-1]+x[a],x[a]);

二、博弈问题
对于博弈问题,经过这几天的学习,给我最大的印象就是它是一个规律性十分强的题。有时会非常简单,只需要去列举一些情况,问题便会迎刃而解。
1、拿纸牌问题
题目大意:
两个人玩游戏,有n张纸牌,每人一次可以拿1到m个,问开始时第一个取的人会不会输
????????????????????怎么办呢????
刚开始看到这种问题,首先想到的是这这会不会是二维贪心问题呢??
毕竟是两个人都有自己的想法,都想赢得比赛。但如果去贪心,那么就把简单问题复杂化了,思路远比贪心简单。
我们可以这样思考,不是一次最多只能拿m个吗,那么你最少要拿1个,这样以来m+1个不就是必输点吗!!!只要我能去给对手构造m+1,那么对手不就一定输了吗。代码也十分的简单,只需要判断n%m+1是否没有余数,是的话第二个人一定赢,否则第一个人一定赢。

if(n%(m+1)==0)cout<<"second win<<endl;
else cout<<"first win"<<endl;

2、取石子游戏
题目大意:1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
分析:
n = 2时输出second;
n = 3时也是输出second;
n = 4时,第一个人想获胜就必须先拿1个,这时剩余的石子数为3,此时无论第二个人如何取,第一个人都能赢,输出first;
n = 5时,first不可能获胜,因为他取2时,second直接取掉剩下的3个就会获胜,当他取1时,这样就变成了n为4的情形,所以输出的是second;
n = 6时,first只要去掉1个,就可以让局势变成n为5的情形,所以输出的是first;
n = 7时,first取掉2个,局势变成n为5的情形,故first赢,所以输出的是first;
n = 8时,当first取1的时候,局势变为7的情形,第二个人可赢,first取2的时候,局势变成n为6得到情形,也是第二个人赢,取3的时候,second直接取掉剩下的5个,所以n = 8时,输出的是second;
…………
从上面的分析可以看出,n为2、3、5、8时,这些都是输出second,即必败点,仔细的人会发现这些满足斐波那契数的规律,可以推断13也是一个必败点。

#include<iostream>
using namespace std;
int main()
{    
    int fib[50] = {2,3};
    for(int i = 2; i < 45; i++)
        fib[i] = fib[i - 1] + fib[i - 2];
    int n;
    while(cin>>n)
    {   
        if(n==0)break;
        int flag = 1;
        for(int i = 0; i < 44; i++)
        if(fib[i] == n){
            flag = 0;
            break;
        }
        if(flag)cout<<"First win"<<endl;
        else cout<<"Second win"<<endl;
    }
    return 0;
}

总结:
通过这几周的学习,发现越来越难,思考量实在太大了,有时一个题即使自己有思路了也会出现细节上的错误。从这几天每天晚上的训练也让我感觉到这些题目有很多都是思维题,细节题,有时一个细节想不到,它就永远不会ac。就目前阶段,最让我头疼的还是dp题,也可能是自己做的少,每次看到dp的题目就会排斥它,就觉得自己很难做出来。但我感觉这种题很难做到突击训练,它需要从最简单的题目做起,这种题目需要积累,它不像贪心那样,有基本的流程,dp的题就特别难思考。在接下来的时间,自己要做的就是多做题,熟能生巧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值