分类目录归档:acm

[POJ 1286]

Necklace of Beads

第一题Polya计数,好有意思的定理。

题目输入很简单,就是一个n,问n个在环上等距放置的球,可以用3种颜色染色,用各种方式(旋转/翻转)去除重复染色方案后,输出总染色方案数。

看完Polya定理后,结合题目条件想了一下便推出了解:

若另某个位置为0,顺时针方向依次为位置0、1、……、n-1,那么,可以分别另0号球于0、1、……、n-1开始,沿顺时/逆时针方向依次摆下其余编号的球,那么总共有2n种不同的置换方案的,这也是覆盖了全部置换的情况。接下来便是对每种置换方案计算各自的轮换数了,这步也很简单的,对每个不在自己位置且未标记的球迭代求对应的轮换,每次找到新的轮换都将对应方案的轮换数增一即可。最后sum(pow(3,ci))/2n便是结果了。

本来以为可以1A的。。。结果没考虑n=0的情况。。。还是太嫩了。。。

但即使AC了也只是略微明白定理如何使用,证明看了一下发现不太好懂,就暂时放下了。

[POJ 2229]

Sumsets

数学递推。

看完题面第一时间的想法就是O(NlgN)地枚举。。。竟然过了。然后看了下status,有0ms的,便开始想数学解法。。。

没什么思路后,开始观察规律:将2、4、8、16、32打表出来,没发现什么规律。就把1到32的全打出来了,发现两数之间的计数值间的差值有点像等差数列,又猜了一下便发现递推公式是:
f(x)=1, x=0
f(x)=x-1, x%2==1
f(x)=f(x/2)+f(x-2), else

但AC了之后依然不知道为什么式子是这样的。。。又经过漫长的分析之后发现,可以将x的表示方式分为两类(这里x为偶数,奇数情况容易发现即为x-1的计数值):
(1)合式中含有1的:这种情况x的表示形式和x/2是相同的,其中x合式中的2便对应着x/2合式中的1,依次类推;
(2)合式中不含1:这里x的合式中至少要有2个1(因为x是偶数),因此其表示形式可以由x-2的形式+1+1得到;

以上两类刚好能不重复地包含x的全部表示形式,因此便能得到前面的公式了。

[POJ 2057]

The Lost House

树形DP。很好的一道题目,这里认真写写。

题目很长,大意就是有一个树形图,出发点是树根,目标(即终点)以等概率的形式分布于每个树枝末端,要求的是在未知哪根树枝为终点的情况,最优搜索方式下的期望总搜索长度。另外这里还有一点附加的是,每个分支点可能有提示,即到达该树枝时可以立即知道目标是否在该子树下。

先不考虑存在提示的情况。对每棵子树的搜索可能有两种结果,成功或失败。若成功则要分别计算下一层每个子树的成功或失败情况,并计算出最优搜索下的搜索长度,这里记为ai;若失败则简单将所有子树的搜索总长累加即可,这里记为bi。另外,由于子树搜索成功是要计算概率的,这和子树上包含的叶子总数有关,因此需要维护一个子树所含叶子总数ni。由此便可以开始分析ai的计算了。

ai的计算也可以分为两部分,首先由概率公式可以得到,每棵子树j搜索成功的这部分概率是不受子树间先后顺序影响的,其对最优搜索期望总长的贡献总是(aj+1)*nj/ni。那么,这里最关键的就是搜索失败时的处理情况。这是本题的难点。

当已知某棵子树搜索失败时,后续子树搜索失败的概率会变低,若设剩余子树所含的总叶子数为nl,具体期望值公式为(bj+2)*(nl-nj)/ni,也即终点应在剩余的nl-nj个叶子中的某个上。这里是确定最优搜索方式的关键,我想了很久。若设l为剩余子树编号,尝试了每次根据(sum(bl+2)-bj-2)*(nl-nj)的最大值来贪心选择下一次搜索的子树,这里的思想是每次都使剩余期望值降低最多,能过test cases,但是submit后WA。这里思路一下子卡死了。便看了discuss,恰巧是magic_pig拯救了我(他是之前情书作者kinfkong的中大校友),他提到要按(bj+2)/nj排序。尝试了一下果然AC了。。。

晚上仔细想了想这样能求解的原因。观察到其形式很类似轮换不等式,朝这个方向推了一下之后便明白了:若交换搜索中次序相邻子树的j和k,是不影响之前和之后其他子树的期望值的。同时,每个子树受其他子树影响的量是bj+2,影响其他子树期望的量是nj,由此便能通过对(bj+2)/nj进行排序来确定最优搜索顺序了。实在是太过精巧的思路。

到这里为止,本题的所有trick基本上排除完了。bi的计算则是简单的sum(bj+2),若该子树存在提示,取bi=0即可。

这样的题目,完全弄懂之后总是有种难以言喻的快感… 马上又要开始做项目了,还能像现在这样无忧无虑做题的时间也不多了。

[POJ 3254] & [POJ 2411] & [POJ 1185]

3题都是状态压缩DP,难度是循序渐进的,便一起写了。

Corn Fields

这题属于最基本的状态压缩DP。直接做的时候想了很久也不清楚如何构造DP,看了discuss后明白到,要使用位向量的形式表示一行的情况,并可以用&的方式判断是否能累加上一行的计数值。由此复杂度是O(m*2^n*2^n),便能过了。这种题目太特殊了,某行的情况必须要在一个32位的int里放得下,并且每一行的向量总数不能太多,否则指数时间复杂度是难以承受的。但好像也没有别的办法可以有效求解这类问题。

Mondriaan’s Dream

和3254类似,但这里需要用^来判断竖直方块的情况(平行方块是00,垂直方块是1),并且记录的状态是亦或运算后的结果,这样才能反应下一层看到的上层情况,最后输出末行状态全0的计数值即可。

炮兵阵地

和3254类似,但增加了难度:需要考虑前两行的情况,同时输出的是盘面最大值而不是计数值。虽然有前面两题的基础,但推状态转移时还是想了一下的:需要用一个dp[i][k][kk]形式的数组,记录第i行向量为k且i-1行向量为kk时的最值。由此便能在根据前两行的向量情况更新可行最值了。

但到这里为止算是出了思路,提交的时候还是分别被卡了内存和时间:内存方面,将dp改成滚动数组便ok了,时间方面,当构造第i行最值时记录其可行的解向量,这样到后面便可以根据记录下来的解情况来更新而无需枚举所有2^m*2^m种情况了。

[POJ 3034]

Whac-a-Mole

“打地鼠”,好神奇的一题DP…

题意是,有一个n*n的区域,在时刻1~10之间,每时刻会出现一些地鼠。有一个初始位置任意的锤子,在每个时刻,你可以将锤子直线移动最多d个单位,起止点都必须是整点,若有地鼠出现在移动路径上(必须是坐标x,y刚好在直线上),则可以将它们敲掉得分。目标是求t时候后最高得分。

数据规模不是很大,但分析了下复杂度,发现竟然是O(kn^4)级别的……因此需要对某点出发可以移动到的点,以及移动中路过的整点坐标全部预先记录下来……想到这里还觉得太复杂了,怕不是这么做,但是由于d<=5,这样的预处理又是完全合理的……因此开始了一顿乱搞。。。第一次提交WA了之后,读了discuss发现锤子是可以移出区域的。。。稍微作了下修改后便AC了。虽然没有完全能自己想出来,但还是好开心。

使用了同一种边表struct来存多种数据……虽然看上去很乱搞但自己清楚其实是合理的做法,能够AC实在是太好了。 另外今天也去了渣打编程马拉松的北京初赛……地点竟然还是上次北大校赛的北交计算机培训中心。这次的待遇比北大校赛好多了,全程饮料零食供应,中午还一人一份M,只是我肠胃不好也没太敢吃。但如果就比赛本身来说,实在是不怎么样。题目本身说是大数据,其实数据一点也不大,就在百万级别而已。另外就是题目的描述和求解需求都不甚明确,还有前后矛盾的地方……问赛会人员也是越描越黑……最后下午3点的时候上传完解就提前走了。结果到门口竟然还被拦截采访。。。实在是太尴尬了。明显这种非IT公司办的比赛只是为了宣传+招聘,和技术实际是离天隔九丈了……但是来参加这种比赛的我又算什么……

[POJ 1925]

Spiderman

继续DP。

这题看了数据规模后,一直陷在惯性思维里面:总以为复杂度应该是O(N^2),结果怎么想也推不出DP公式。

主要是因为这里每个建筑对应的落点有两个值:x坐标和跳数,这两个值之间并无优劣关系,无法简易去重。考虑使用类似边表的方式将每个建筑的所有落点全部存下,再状态转移,发现内存和复杂度都太大。

然后无奈之下写了个SPFA,状态点是xc[1000001],保存的是到某x坐标时的最小跳数。这种复杂度是O(5000*10^6),略微超了。

最后看了discuss才明白每个建筑对应的起跳点是有范围的,可以算出来的。由此可以将复杂度降到O(5000*sqrt(10^6)),便能过了。

回顾了一下自己没有想出解法的原因,都是太过于沉浸在惯性思维中,无法跳出来从其他角度构建思路。这种毛病一直都有。究其原因,大概是总无法确定先前的思路是否真的无解,从而放不下,每次思路有所进展后又绕回来重新回到死路上,从而让思考的深度和效率都大打折扣。如果能够把握思路,将不可能的不确定性全部排除,也许就能高效地找到正确求解方向了。

我想如果要做到这些,也没有其他办法的,只有多练多想了。

继续努力。

[POJ 3280]

Cheapest Palindrome

典型的DP。

首先用一个DP判断s[i…i+l]是否回文,这可以根据s[i+1…i+l-1]是否回文以及s[i]==s[i+l]得到。这里最外层循环是l,也即回文串的长度。随后是要计算将s[1…i-1]和s[i+l+1…m]转化成完全相同的最小花费,这里可以用另一个DP预处理得到。

[POJ 1191]

棋盘分割

分类里属于DP。分别用DP和搜索两种方法进行了求解。一开始想的就是搜索,加了一个求剩余块均方差的剪枝就AC了,110MS。后面的DP就没有任何优化,直接是整个5维数组全算一遍的,但也0MS了。

搜索:

DP:

[POJ 3373]

Changing Digits

好久没做POJ了。依然跟随POJ题目分类表,这题属于”记忆化搜索”。

貌似是要求高精度,但稍微分析下便知道,由于k<=10,000,那么n最多改变4位数字便一定能被k整除的,因此这里还是使用int除法,递归修改n的每位数字时顺便求出当前模值即可。 搜索的思路很容易出,由条件3和4,易得到搜索的方向:(1)由改变较少位数开始;(2)从高位开始。但是剪枝还是稍微想了一下。最后发现,根据dfs的传入参数,若该次dfs失败,可以用数组标记该参数组合无法满足题意。后续的搜索中,每次根据参数检查数组中对应位置是否已有标记,若是则直接返回0。其实不算难的一题,做的人却不多。另外,也开始习惯用vim敲程序了,开着fedora,窗口A左面vim,右面terminal(编译+运行),窗口B开着chrome读题,很是顺手哈哈。

这段时间有点迷茫,不知道接下来学什么好,能够交流的人也好少。 又买了Effective C++APUE,还是希望能把C++和Linux学得扎实一点。

今天也去了下阿里巴巴组织的大学生课堂,好久没听过课了,加上坐得比较远,实际没听到多少内容。倒是见识到好多牛人,都能各种和讲师交流了。就目前自己了解到的信息,还是很想去阿里的,但现在还不太够格。

亲爱的妈妈,不是我不想追女生,只是现在只能一心追时间啊。

Pythoning…

读了a byte of python后,顺手把a byte of vim也读了。唔,就是用起来还不是很顺手……

刚做的codeforces r195d2c,C一下就过了。。。python就一直TLE。看了下status,竟然没有用python3做的,python2的倒是有几个。明明只是N=10^5,O(N*lgN)的复杂度,却要各种找优化……看来python还真是非高手不能用:用C就是怎样烂的代码只要复杂度没错基本都能过,用python就非得尽量优化不可啊。。。

贴个code纪念一下吧:

这是C的,对比一下就清楚了,python是最后才构建b[]的,每次赋值太慢了: