
日记
今天也是不用考试,也是可以补题。
上午
补状压dp。
P10975 Mondriaan’s Dream solution
我不是就在写题解吗,那我还加 solution
链接干什么
设 表示前 行都被填满了且 行突出到 行的方案数。然后我们可以预处理每个状态 可以由那些状态转移而来,设转移状态的集合为 ,那么有:
初始化 我们就可以得到答案为
P2831 [NOIP2016 提高组] 愤怒的小鸟 solution
设 表示死猪状态为 时的最少消耗。那么有朴素的转移:枚举两个点 ,与原点确定抛物线,然后再枚举一个点 ,看 是否再之前所确定的那条抛物线上。时间复杂度 。总共要算 次,绝对要T。
我们考虑怎么优化。预处理 表示原点与 点和 确定出来的抛物线经过的点,那么就不用枚举 了,时间复杂度 ,算了一下大概要算 次,还是要T。
我们继续优化。我们发现,如果先打 两头猪再打 两头猪,和先打 , 再打 效果事一样的,那么我们就可以预处理一个数组 表示最小(或最大)的 ,使得 i & (1 << x)
为 。那么我们就只用枚举一个 ,时间复杂度降到了 就可以过了。
下午
把昨天的T3改了。
[ARC099E] Independence(就是昨天T3)
因为个人觉得那些题解讲的都太水了,所以就不放 solution
链接了。
他说要把这一个图分成两个完全子图,我们考虑取补图。根据补图的性质,原图每条边的两个端点在补图里都不直接连通或不连通,而题目让我们分两个子图,使得子图里的点两两之间都有边,那么在补图里,他们就是两两都没边。我们就可以发现,就是要把补图二分图染色。在染色过程中我们就可以判断是否无解。而我们又想求最少有多少条边属于一个完全子图(在原图中的),我们设一个子图的点数为 ,另一个子图的点数为 。那么我们的答案就是 。我们考虑dp。在染色的时候,我们记录一下,对于一个二分子图(因为取补图后不保证连通)染成颜色 的点数为 ,染成颜色 的是 。用 bitset
记录哪几个完全子图大小能凑出来(因为 bitset
常数小到爆),然后枚举每一个 记录最大的 ,这题就做完了。
今天是 wgc
的 岁生日,让我们祝他生日快乐!!
晚上
P3959 [NOIP2017 提高组] 宝藏 solution
我们看这个:要让访问所有宝藏屋的代价和最小,那不就是最小生成树吗?但是 如此小,肯定有鬼。一看:代价不直接是边长 ,而是 。这里还多了一个 ,所以贪心不对。因为有这个 ,我们考虑把这个生成数分成 层。我们设 为访问到了第 层,总共访问的状态为 的最小代价。那么我们肯定要从 的子集转移而来。我们枚举每一个子集,并枚举子集里的点,找出它到补集中的任意一个点的最短路,然后把子集里的每一个点到补集的最短路求和,就可以得到答案。形式化讲: ,其中, 表示子集 里的点 到补集 里的任意一个点的最短路,也就是 。初始化会在代码里说明。
123456789101112131415161718192021222324252627
for (int i = 1; i <= n; i++)
for (int j = 1; j < (1 << n); j++)
for (int k = 1; k <= n; k++)
if (!(j & (1 << (i - 1))) && (j & (1 << (k - 1))))
w[i][j] = min(w[i][j],dis[i][k]);//预处理w[i][j]
for (int i = 1; i <= n; i++)
dp[1][(1 << (i - 1))] = 0;//初始化
for (int i = 1; i < (1 << n); i++)
{
for (int j = i & (i - 1); j; j = i & (j - 1))
{
int tmp = 0;
for (int k = 1; k <= n; k++)
{
if ((1 << (k - 1)) & (i - j))
{
if (w[k][j] > inf)
tmp = inf;
else
tmp += w[k][j];//记录子集里的每一个点到补集的最短路求和
}
}
for (int k = 2; k <= n; k++)
if (dp[k - 1][j] != inf && tmp != inf)
dp[k][i] = min(dp[k][i],dp[k - 1][j] + tmp * (k - 1));
}
}