收获:
尼姆博奕:看了好多博客,开始还是比较容易理解,深层面的较难看懂,尤其是尼姆博奕的变型,还有后面涉及SG打表那一块。总结一下自己的认识,简单的跳过。
先看奇异局势,(s[1],s[2],s[3]…… s[n]
),逐步进行异或运算。
若结果为0:那么先手必败;若不为0,那么先手必胜。(也就是说面对奇异局势的人必输)
阶梯博弈算法(尼姆博弈进阶):啃了两三天,头都晕了,证明过程也看的迷迷糊糊。
题目形式:一个楼梯上每层都放有一些石子(硬币也可),每回合将i台阶上石子移动到(i-1)阶上,最后无法移动的输,也就是其他阶都为空,移动第一阶得人赢。
公式:所有的奇数阶上的石子进行异或运算>0,则确定是胜方。因为对于败方来说,若移动奇数阶的石子,胜方便有可将奇数阶石子异或为0,成为奇异局势;若移动偶数阶石子,胜方可将败方移动的石子往前移动,对奇数阶石子无影响。
到这里还好,可根据阶梯博弈变化的题目真的很难,看都看不明白。会继续认真看的。
举一个阶梯博弈变化的题目。一个盒子从1到n被标记,满足:
(A>B&&(A+B)%2==1&&(A+B)%3==0)
,不能移动盒子的人输。
我的思路:想到了阶梯博弈,可还是不会用,发现条件可转化为(A+B)%6==3
,然后举例了1到15数字的移动情况,不难发现最终都会移动到(1,3,4)这个盒子中。经查阅资料:发现要对所有奇数步盒子的礼品数进行异或运算,而所有满足奇数步盒子都满足*(A+B)%6==0或2或5*
,这一步简直太难想了!!
威佐夫博弈:核心公式:(1.0+sqrt(5.0))/2*(a-b)
大体是两堆石头,要么从一堆中取任意数,要么从两堆中取相同数目,先去玩的获胜。这里的奇异局势和尼姆博弈的很像,但没有尼姆博弈难,毕竟尼姆博弈是从n堆石头中取。
需要注意的点:
1.从一堆中取可能可以得到奇异局势。2.从两堆中取一样的石头也可能得到奇异局势。要根据题目要求灵活改变。
k倍动态减法:这个同样非常难,看了好久都还是很懵,也能看做是斐波那契博弈的变型,只是不再是两倍了。当k=1时,2的i次方都是必败点,将n化为用二进制,每次拿走最后的一个1,而对手不能拿走倒数第二个1,因为他拿的数量不能多于你拿的;当k=2时,斐波那契数列都是必败点;当k>=3时,开两个数组a[n],b[n]
,a[n]存的数字都是必败点,b[t]是a[1到t]所能表示的最大数,最后要使得
b[i]=b[t]+a[i] (a[t]*k<a[i])
具体实现代码:(还没完全理解),目前还是先记住再说,适用性广
while(n>a[i])
{
i++;
a[i]=b[i-1]+1;
while(a[j+1]*k<a[i])
j++;
if(k*a[j]<a[i])
b[i]=b[j]+a[i];
else
b[i]=a[i];
}
记忆化搜索:看了很多资料,感觉还是难在理解递归式状态转移方程那里。
1.概念其实很好理解,搜索过程中,避免重复计算,记录一些状态的答案,在接下来状态中可以直接使用。2.记忆化搜索只是类似于动态规划,不是属于。具体是倒着的递推式动态规划。
题G:
Y题收获:题意省略
1.开一个二维数组,如果要对其中某一行进行初始化memset(dp[1],0,sizeof(dp[1]));
这个代码是可以实现的,但要注意:只能初始化为0,若初始化为1或其他数,会出现乱码。
2.然后就是我的思路:首先确定要定义一个二维数组dp[i][j],想到用i来控制最大值为多少的方案数案数,j来表示具体取得末尾的数字。记录的是方案数,当i等于k时,表示最大数字为k,再从1到n遍历所有方案数累加
大致思路没问题,有些细节没想到:1.如何控制取得的数,由于题目中后一个数字要整除前一个数字,于是又要加一重循环来控制乘的跨度(1,2,3…)。问题看似处在代码实现上,其实还是思路没想好。
3.就是取模要及时,每次累加都要。一个注意点。
F题收获:给定一个字母矩阵,规定可替换的字母,要求所有字母一样的最大子矩阵的面积。(花了一个上午,弄得我心累的一题)
首先是题意:和我理解的有偏差,我并没从题目看出求面积的意思,我认为只要求出最大子矩阵相同字母的个数即可。----->>>方向上就错了,虽然自己也不太会做。
我的思路:虽然自己没思路,但也想了好久。我发现,替换的情况有很多,肯定是分类讨论。也想到了将字母全变成a或b或c,全变成a去找最大子矩阵(b,c同理),到这里是对的。然后我想直接去找字母见得关系,比如最大子矩阵a,如果左边或上边是a累加1,其实行不通,这只是在记录a的次数而已。
查了题解后,题解都看了好久才懂,很多困扰的难点都有了解释。
1.为了方便记录,用数字代替字母,出现a则标记为1;然后先只考虑纵向上a出现的次数,若a上方也是a,累加1。
2.更妙的是他计算面积的方式。因为在步骤1中考虑了纵向,接下来考虑横向就好。dp[i][j]是第i行第j个字母,所表示的值便是这个子矩阵的高,以它为中心,然后向右取比它大的,向左取比它大的,得到最大宽,计算面积取最大。是之前没见过的查找方式!!!(关键是另外开两个数组)
for(int j=1;j<=n;j++)
{
while(dp[i][j]<=dp[i][l[j]-1])
l[j]=l[l[j]-1];
}
for(int j=n;j>=1;j--)
{
while(dp[i][j]<=dp[i][r[j]+1])
r[j]=r[r[j]+1];
}
L - Sonya and Problem Wihtout a Legend 收获:
本题用了离散化和一些数学知识,看完资料后,才懂了一点,慢慢用到了高数知识和结论了。题意:要求用最少的步数构造一个严格的上升数组。
数学结论:如果是上升数组,肯定满足 (a[j]-a[i])>=(j-i)
,所以便得到 (a[j]-j)>=(a[i]-i)
,所以用数组b[n]存储(a[i]-i)的值,并进行排序。abs(a[i]-b[j])为正在处理第i位时以b[j]为基准最小花费步数,而dp[i][j]为处理到第i位时以b[j]为基准总体上最小花费步数,是需要加上 上一步同样以b[j]为基准总体上最小花费步数,和同样处理到第i位时以b[j-1]为基准最小花费进行比较取最小。
构造状态转移方程:dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(b[j]-a[i]));
4.14训练赛:
1.我负责的那道题没有过,因为忽略了一种情况,一直卡住,所以比别人少做一道题,很自责,加油吧!!!
思路:大致都是对的。两端的树就往两边倒,中间的树减去相应距离或加上相应距离不和两端的树相交,然后累加。之所以没过:非要用ans去记录才差值想着方便一点,结果把自己搞晕了,因为ans的值需要不断更新。
2.D题我的思路是找规律,规律已经快出来了,但还需要时间。队友直接模拟,我觉得也很好。卡住我们的难点:要过菜品个数进行排序(保证剩余的的菜品先进行配对,这样才能保证最多,参考样例:2 3 2)。
3.A题我们思路是完全正确的,但是代码实现上有问题,就被搁置了,没想到别人都过了,特别亏。其实代码实现也很简单,一个数找0和8,2个数两重循环枚举遍历,三位数类似。
教训:
1.有些问题是自己想得复杂,不是题目复杂,可能和心态有关,心态要放平。
2.如果确定思路正确,就不要轻易的放弃代码实现,代码是可以慢慢调的。
3.我负责那题就是我把问题复杂化,定义了一个ans记录位置
4.15训练赛:
1.一个代码实现出现了问题,整个队浪费了将近40多分钟。忽略了一个问题,如果这个‘1’是在最后出现的,那么没有一个‘0’去累加这组1的长度。难受了!
**4.16训练赛:**主要两个差距,前半段一直是领先的,后面心态问题特别着急,导致一个简单题目没做出来:另一个是H题,没写出来,和其他组的差距,要努力追赶并超越!!!
H题:
两方面原因:1.看题不仔细,看的有点快,没发现输入一栏中的条件
1<=b[i]<=2n
。2.还有就是代码实现,没法将思路很好的转化。收获到一个没想到的代码实现形式。
for(int i=1;i<=n;i++)
{
cin>>a[i];
m[a[i]]=1; //将出现的元素标记!!
}
int temp;
for(int i=1;i<=n;i++)
{
b[i*2-1]=a[i]; //若一个数组是另外一个数组的两倍,可根据特征记录)
temp=a[i];
while(m[temp]==1)
{
temp++;
}
(后面代码就很平常,省略)
技巧,注意点:
1.除要用double。
2.异或运算
1.异或的一个运算规律: A^B=C --->>> C^B=A;
2.‘^’ 的优先级小于 '> , <'
,注意加 '( )'
赛后补题:(不放代码,主要是思路,错误的点和收获)
一.C. Divisibility by Eight
思路:是完全正确的。能被8整除的数:1000+b;只要保证b能被8整除即可。分别遍历1位数,2位数,3位数.(采用循环方式,暴力做即可)
错误的点:个位如果取了这个数字,十位数是从这个数字前开始取,百位也是从十位取得数之前取。
收获:(整理一下数字特征)
能被2整除的数:奇,偶取模于2即可。
能被3整除的数:各个位上数之和能被三整除。
能被4整除:最末两位数字能被4整除即可。
能被5整除:末位为0或5.
能被6整除:同时能被2和3整除的条件。即是偶数,且各个位数之和能被3整除。“12”同理。
能被7整除:abc def ghi jkl,满足(jkl-ghi+def-abc)能被7整除。“13”同理。
能被8整除:最后三位数字能被8整除。
能被9整除:各个数字之和能被能被9整除。
能被10整除:末位为0。
可被11整除:(奇位数字和-偶位数字和)能被11整除。
二:B. Balanced Array(水题跳过,过了一遍代码,当时敲的时候思路不清,整理一下)
三…A. Candies(水题,唯一要注意的点)
收获:pow()函数的计算方式存在误差
---->>>处理方式:加上一条常变量const int eps=1e-6;
(放主函数外面)。不然就用double
四:C. Alternating Subsequence
我的思路:
1.首先要保证取得子段最长。将观察发现,如果第一个元素是正,那么最长的便是“+-+-+-+-
”这种形式;如果第一个元素是负,那么最长的便是“-+-+-+-+-+
”这种。
2.之后两种实现方式,第二种更巧妙一些。都是找同符号下最大的元素累加。
关键实现方式:如果找到了正号下最大的元素,那么是在负号语句中加;同理,负号最大的元素在正号语句中加。不然无法实现加和最大,卡了好久。还有最后的和并未加上最后一个找到的同符号最大值。
第二种实现形式:用乘积来判断正负号,如果和前一项同号,乘积为正,然后找最大值;之后再乘积为负的时候加上去。
五.Tetrahedron
做了那么多的dp题目,发现自己根本就不会dp!!!学的好乱,一定要花时间整理。即使本题是一个很水的dp,我都做不出来。有很多零星的想法,代码写不出来,真的会很失落。简要讲一下思路:
首先:确定dp[i][j]表示的含义,第i步时到达某点的最大方案数。
其次:找dp转移方程。因为一个点有三种方式到达,所以要再加一重for循环。
dp[i][j]+=dp[i-1][g] (g表示从除自己所处点之外某个点,表示上一步g点的最大方案数)
虽然每次训练赛因为各种原因都输的很不甘心,但只有不断的完善,找到队伍的问题,总结自身的原因,不断鞭策自己,不断追赶别人,也还不错。但说到底,没有人愿意输,相信一定会追上并超越,不用怕爬的慢,不为给他们看!!!