AtCoder Beginner Contest 408
A,B
H₂O题。
A 题直接秒,B 题直接用 STL,甚至比 A 题短。
C
观察题意后发现本题其实就相当于求防守最薄弱的城堡。
看到区间修改多次最后查询,用差分解决就行,最后再用前缀和求值取 min。
当然你也可以用线段树来做
D
俗话说得好,有错误贪心就一定有正确 DP。
在赛时我先试图写了贪心,但是失败了
所以,我们想一下状态定义。
状态定义
按照直觉,DP 数组的第一维一定表示他之前的最优解。
注意到这个下标的最优解跟前面的连续 1 序列个数有关(有连续 1 序列之后就不能再有了)。
所以第二维就是前面的连续 1 序列个数了。
我们还可以发现这个下标的最优解跟前面的一项有关,如果有连续 1 序列且是 0,那这一位就不能放 1 了。
所以汇总一下:
int f[N][2][2];
// f[i][j][k]: 前 i 个满足 1 的序列有 j 个并且第 i - 1 项为 k 时的答案
状态转移
先考虑前面没有连续 1 序列的情况。
没有连续 1 序列
所以这一位只能填 0。
前一位同样,而且也得满足「没有连续 1 序列」。
if (!s[i])
{
f[i][0][0] = f[i - 1][0][0];
}
else
{
f[i][0][0] = f[i - 1][0][0] + 1;
}
有连续 1 序列
如果这一位填 0,那么上一位必须满足「有连续 1 序列」,至于填不填 1 无所谓。
如果这一位填 1,那么上一位必须满足「有连续 1 序列」并且填 1,或者不满足「有连续 1 序列」并且填 0。
if (s[i])
{
f[i][1][1] = min(f[i - 1][0][0], f[i - 1][1][1]);
f[i][1][0] = min(f[i - 1][1][0], f[i - 1][1][1]) + 1;
}
else
{
f[i][1][0] = min(f[i - 1][1][0], f[i - 1][1][1]);
f[i][1][1] = min(f[i - 1][0][0], f[i - 1][1][1]) + 1;
}
赛后我发现原来不用 DP 也能做,用 DP 做感觉就是 E 题的难度了。
E
赛时我先试图使用了最短路算法求,但是发现无法通过样例 2(原因是优先队列无法满足按位或这种对条边不一样的情况)。
于是我们可以换换思路,何不使用类似二分答案的方法,从答案的角度入手能?
想要判断一个答案是否可行,只要判断是否有一条 \(1\) 到 \(n\) 的路径满足所有子元素都是答案的子集就行了。
接下来我们可以贪心地想,先让答案包含所有元素,然后再每次移除一个元素,判断是否可行,如果可行就永久移除。
最后答案必定是最小的。
判断可行时只要只将满足边权是答案的子集的边加入临时的图里,然后再判断 \(1\) 和 \(n\) 是不是同一个连通块即可。
后面的题作者没有写,请尽情谅解

浙公网安备 33010602011771号