课程总结(第五周)

收获的dp技巧:
1.根据题目意思,首先要考虑数组元素的含义。
2.找出数组元素的关系式(也就是状态转移方程)。比如dp[i][j]通常和dp[i-1][j],dp[i][j-1],dp[i-1][j-1]有特定关系。要根据具体情况找出规律。
3.找出初始值,感觉这一步同样非常重要。有点题目初始值定义为无穷大(0x3f3f3f3f3f),有些定义为0或1。

最简单实用的博弈判断技巧:
我看了好多种对于必胜点(P|positive)和必败点(N|negative)的定义。找到以一种适合我的。

1.只要当前状态可以转移到的状态中有一个是必败点,那么该点是必胜点。
2.反之,如果当前状态能转移到的所有状态都为胜态,那么该状态为败态。

(这就暂时规避掉了两个选手谁会获胜的考虑。如果每次到一个点,都要考虑是上一位选手获胜(必败点),还是下一位获胜的话(必胜点),这样也可以,就是思路会乱)。

SG函数:定义难理解。简单来说就是一个函数,利用了递归。x节点的SG值是去除x的后继节点的SG值后的最小非负整数。
SG(x)=0,当前节点x为必败点。
SG(x)>0,当前节点为必胜点。

(非常有用,但具体如何使用sg打表格解题,还要慢慢体会。虽然找规律也很方便,但也要掌握套路)

巴什博弈,斐波那契博弈。有些懂了,有些没必要懂。这些博弈有很多通过数学原理进行论证,有些理解不了。但我把关键性的结论记住,我的目的是能应用到题目中,那些证明过程不懂就算了。就深入浅出的总结一下收获:

巴什博弈:n个物品,每次取不超过m个,取完的人赢。(题目分为很多,有的是对每次取得物品进行限制,局限到一个范围中;有的是规定取完的人输)。
*核心公式:(m+1)r+s (r为自然数,s小于等于m)
只要是(m+1)的倍数就是必胜点,而对于上一个选手来说只要将物品取到(m+1)的倍数就一定会获胜。简而言之:if(n%(m+1)==0),后取者赢;否则,先取者赢。

斐波那契博弈:n块石头,每次取的石头数不超过前一次取的人的两倍,取完者胜。
总之,只要是斐波那契数字都是必败点,其余为必胜点
还是要根据具体问题分析。


J题收获:
此题类似于连续子段求最大的那题,而本题要求连续子段绝对值和最小
---->>>1.我的想法是用一个dp[n]数组记录每个位置绝对值最小的情况,然后用for循环进行比较,案例是过的,但是无论怎么改都无法ac。最后发现,因为dp[i-1]可能是经过绝对值的,下一个a[i]加上去时,会导致dp[i]结果不准。思路正确,但是实现方式有问题。

 for(int i=1;i<=n;i++)
        {
            dp[i]=min(abs(dp[i-1]+a[i]),abs(a[i]));
        }
        int minn=1000000;
        for(int i=1;i<=n;i++)
        {
          if(dp[i]<minn) minn=dp[i];
        }

----->>>2.然后发现题目给出的数组其实很小,只有1000。是可以进行暴力解题,分别就出所有子段和进行比较,便能求出最小的。

 int minn=abs(a[1]);
        for(int i=1;i<=n;i++)
        { int sum=0;
            for(int j=i;j<=n;j++)
            {  sum+=a[j];
                if(abs(sum)<minn) minn=abs(sum);
            }
        }

----->>>3.但是我还是想用DP去解决这个问题。共n个数,可以求出每个数到这个数为止的最小绝对值,再用for循环进行比较。很好的实现方式,有时虽然min()函数会很方便,但会忽视掉很多细节处理

for(int i=1;i<=n;i++)
        {
            dp[i]=abs(a[i]);
            num=a[i];
            for(int j=i-1;j>=1;j--)
            {
                num+=a[j];
                if(dp[i]>abs(num)) dp[i]=abs(num);
            }
        }

A题收获:
一头牛获得跳高能力,奇数时间服用药剂能力上升,偶数时间服用药剂能力下降。
我的思路:
1.开始没有理解意思。后来想明白,牛每个单位时间都要服用药剂,但可以选择跳过药剂,读懂题意,下一步。
2.因为时间分奇偶服用药剂,开始误以为是二维线性,实际上只需要开两个表示奇偶的数组即可,分别表示在奇数点最大跳高能力,偶数点最大跳高能力。
3.思路已经挺清楚了。但一直考虑时间这个因素,认为偶数时间要减,奇数时间加,这其实没必要,只会更加混乱,之后明白,因为中间会跳过药剂,所以给出药剂的时间不是真正服用药剂的时间。所以可以忽略这个因素。
3.只要关注奇偶数组就可。列出状态转移方程:奇数组减去药剂值和偶数组当前值进行比较;偶数组加上药剂值与奇数组当前值进行比较。
感觉这题理解比较困难,但是理解后的实现其实是很自然的。

E题收获:
1.我一开始的思路是分成几种情况讨论,若雇佣人数和上一周期相等,则直接发薪水即可;若是本星期雇佣人数大于上一星期人数,则直接雇佣差额人数就好;若是本星期雇佣人数小于上一星期雇佣人数,用min()来比较裁人的花费和直接不裁直接发薪水的花费。过了样例,但是一直wr。
2.然后发现,自己把问题想的简单了,因为本星期的裁人会影响下一星期的雇佣人数,比如:本星期裁掉的人数,其实并不是和上一星期的差额,只有裁掉这个范围内适合的人数,才能保证下一星期雇佣的人花费少,并且本星期需要的人数也并非是个固定值。虽然对于本星期的花费并非最优,但从整体来看,是最优的。
3.情况一下子就复杂了起来。只能用for循环对每一种情况逐一排查,看了下别人的思路,其实方法有点像暴力求解,把所有实际需要雇佣的人便利到最大人数,再把上月需要雇佣的人数遍历到最大,情况特别多,再把这个星期所可能雇佣到的人进行比较,取一个最小值。

本题还有一个难点,因为要求的是最少花费,要将数组设定为无穷大。于是学到了一种无穷大的最好实现方式 memset(dp,0x3f3f3f3f,sizeof(dp));

X题收获:理解题目后,发现不知道怎么去实现。想了几种方法也都是行不通,并且都是围绕着删除某个’a’或‘b’,这样做会很难,自己也不会。最后发现,只要做好统计即可.
dp[0][i]表示记录‘a’开头的字符数目。dp[1][i]表示记录第一组‘a’结束后‘b’加进去的字符数目。
dp[2][i]表示记录b统计到最大后‘a’加进去的字符数目。
感觉很难想到,三大类共六种情况。

codeforces上几道题:
div.2的第一题:一段字符,插入一个‘a’,使其不是一个回文字符,若是,输出”NO”,不是,输出“YES”。
对于我的两个难点:
1.开始没想明白怎么在这段字符中随意插入“a”---->>>之后明白使用insert()即可实现。
2.因为之前都是判断一个字符串是不是回文字符串。此题要求插入“a”使其不是一个回文字符串,因为插入a的方式的方式有很多,脑子一下子没转过来。
其中一个方式:从字符串末尾开始遍历,找到一个不是“a”的位置,在字符前一直对应的位置插入a。轻松的构造了一个非回文字符串,特别好的方法!!!

4.7 训练赛:
Array with Odd Sum :这题题目一直没看明白,是队友告诉我的。发现自己是被困扰在“:=”这个被题目定义的符号上,我以为这是运算符我不知道,还误以为是“/=”,两个数做除数,真的不应该。
一句话说明大意:一个数组数可相互替换,要求和为奇数。
思路是一下就有了,若全为偶数或者n个数全为奇数且n为偶数,则输出“NO”,其余输出“YES”。
收获:看题时不要加上自己的主观臆断,题意的理解真的非常重要,这在我们做D题时也充分体现。

第二题一下便过了,没什么波折。

D - Fight with Monsters:
这题我们经过深入思考,最后发现是一个(分类讨论+贪心)的问题。排除多种方法,我们最后想法是:用怪兽的血量对两人的攻击和取余,若是在我攻击范围内我直接打死;若在二人攻击范围之间,再对a进行取余,存入数组(自然联想到要进1)的问题;若大于b的攻击范围,我直接打死。
这个思路显然是错的,删删改改,代码已经不满足题意了,但是最后还是过了这个题,也是很奇怪。
问题点:
1.a和b的大小不确定。2.当时还没有判断出谁先打怪兽。
然后舍弃了分类讨论: x[i]=((x[i]-1)%(a+b))/a;这个式子,直接取代了三种讨论的情况,然后经分析样例,发现每次都是a先打。但是,我一直觉得这个式子有点问题,比如:a=2,b=3,boss血量是50,我为了自己打死这个怪兽当怪兽血量剩3的时候就用武器,用2次;如果用这个式子得话怪兽血量是4的时候,用两次武器。虽然都是2次,但是 是对于不同的怪兽血量而言。这里还是涉及到进1的问题。

4.8 训练赛:
第一题我们是第一个过的。然后挑了道我们所学范围之外的题目,涉及位运算,虽然我们的思路是对的,但是代码由于知识点还没了解,敲不出来。然后又去把另一道水题过了。但已经相对落后很多了。

4.9训练赛:
主要分析B题。修马路,要保证修的马路一般是好的。题意关键在于:n个马路是必须要修的,要求保证一般是好的。刚开始题目理解错了,只是简单分类讨论,
1.将n小于g时,直接输出n;大于g时。
2.求出一半马路是好的所要天数。
感觉没什么问题,但一直不过,问题出在(1)上面。想了真的很久,发现:如果好的天数大于等于坏的天数,不管怎么修,都能保证好的马路占一半以上。这一点一直被忽略,因为思路一直放在好的天数上面,没有去想不好天数所起到的作用。

做题目的顺序很重要!!!


感慨良多,最后只想说:
摒弃杂念,想要什么就要努力争取;
全力以赴,使最初的选择显得正确。

你可能感兴趣的:(课程总结,算法,leetcode)