题面:洛谷
Solution
首先,我们可以先康一康题目的数据范围:n<=18,应该是状压或者是搜索。 事实上,这题搜索和状压DP都是能做的。 (因为搜索在我心中留下了阴影(斗地主),所以在这里,我讲状压DP的做法) 根据我们以往设计状压DP的经验,我们可以很轻松地设计这一题的状态: 设f[i]表示打下的猪猪的状态为i的方案数,(状态在这里用二进制方式来表示,例如:00101表示打下了第1和第3只猪) 那么有: $f[i] = min(f[j])+1$ (j为i的子集) 这里用到一个枚举子集的技巧,对于一个状态i,它可以这样枚举子集: for(int j=i;j>=0;j=(j-1)&i) (至于证明,你可以在草稿纸上画画,很快就会发现它的精妙了) 那我们怎么判断能否从状态 j 转移到 i 呢? 首先,根据数学常识,我们需要3个x不一样的点才能确定一条抛物线。这题已经固定了原点了,所以我们还需要两个点来确定一条抛物线。 如果j与i只有一个或两个x不同的点 是不同的,那显然是可以转移的。 对于有两个以上的点,我们可以用前两个点通过解二元一次方程来计算函数的a与b,然后再去一个一个判断每个不同的点是否在这条抛物线上。 对于如何解二元一次方程…(这应该是数学常识吧) 复杂度$O(3^n*n*T)$ 显然TLE,事实上,这样做只能得60分。 那怎么优化复杂度呢? 刚刚的枚举子集显然是不可行了,那我们可以换个思路。 我们可以枚举点。 对于某一种状态,我们肯定可以枚举两个(或一个)没有用过的点去构成新的抛物线从而更新其他的状态。 这样子,我们成功地把复杂度降为了 $O(2^n*n^2*T)$ 依然过不了,事实上,这样做能得85分。 上一个作法已经和正解很接近了。 我们可以考虑这样优化方程: 这样子,我们复杂度就降为了$ O(2^n*n*T)$ 就酱,我们就可以把这道题切掉啦(´▽`)ノ
Code
1 | //Luogu P2831 愤怒的小鸟 |