日记 今天也是不用考试,也是可以补题。
补状压dp。
我不是就在写题解吗,那我还加 solution
链接干什么 设 d p i , j dp_{i,j} d p i , j 表示前 i − 1 i - 1 i − 1 行都被填满了且 i − 1 i - 1 i − 1 行突出到 i i i 行的方案数。然后我们可以预处理每个状态 j j j 可以由那些状态转移而来,设转移状态的集合为 a j a_j a j ,那么有:
d p i , j = ∑ k ∈ a j d p i − 1 , k dp_{i,j} = \sum\limits_{k \in a_j}dp_{i - 1,k} d p i , j = k ∈ a j ∑ d p i − 1 , k 初始化 d p 1 , 0 = 0 dp_{1,0} = 0 d p 1 , 0 = 0 我们就可以得到答案为 d p h + 1 , 2 w − 1 dp_{h + 1,2 ^ w - 1} d p h + 1 , 2 w − 1
设 d p i dp_i d p i 表示死猪状态为 i i i 时的最少消耗。那么有朴素的转移:枚举两个点 i , j i,j i , j ,与原点确定抛物线,然后再枚举一个点 k k k ,看 k k k 是否再之前所确定的那条抛物线上。时间复杂度 O ( T × n 3 × 2 n ) O(T \times n^3 \times 2^n) O ( T × n 3 × 2 n ) 。总共要算 18 3 × 2 18 ≈ 1.5 × 10 10 18^3 \times 2^{18} \approx 1.5 \times 10^{10} 1 8 3 × 2 18 ≈ 1.5 × 1 0 10 次,绝对要T。 我们考虑怎么优化。预处理 v i s i , j vis_{i,j} v i s i , j 表示原点与 i i i 点和 j j j 确定出来的抛物线经过的点,那么就不用枚举 k k k 了,时间复杂度 O ( T × n 2 × 2 n ) O(T \times n^2 \times 2^n) O ( T × n 2 × 2 n ) ,算了一下大概要算 8.4 × 10 9 8.4 \times 10^9 8.4 × 1 0 9 次,还是要T。 我们继续优化。我们发现,如果先打 1 , 3 1,3 1 , 3 两头猪再打 2 , 4 2,4 2 , 4 两头猪,和先打 2 , 4 2,4 2 , 4 , 再打 1 , 3 1,3 1 , 3 效果事一样的,那么我们就可以预处理一个数组 l o w i low_i l o w i 表示最小(或最大)的 x x x ,使得 i & (1 << x)
为 0 0 0 。那么我们就只用枚举一个 i i i ,时间复杂度降到了 O ( T × n × 2 n ) O(T \times n \times 2^n) O ( T × n × 2 n ) 就可以过了。
把昨天的T3改了。
因为个人觉得那些题解讲的都太水了,所以就不放 solution
链接了。 他说要把这一个图分成两个完全子图,我们考虑取补图 。根据补图的性质,原图每条边的两个端点在补图里都不直接连通或不连通,而题目让我们分两个子图,使得子图里的点两两之间都有边,那么在补图里,他们就是两两都没边。我们就可以发现,就是要把补图二分图染色 。在染色过程中我们就可以判断是否无解。而我们又想求最少有多少条边属于一个完全子图(在原图中的),我们设一个子图的点数为 a a a ,另一个子图的点数为 b b b 。那么我们的答案就是 a n s = a ( a − 1 ) + b ( b − 1 ) 2 ans = \frac{a (a - 1) + b(b - 1)}{2} an s = 2 a ( a − 1 ) + b ( b − 1 ) 。我们考虑dp。在染色的时候,我们记录一下,对于一个二分子图(因为取补图后不保证连通)染成颜色 1 1 1 的点数为 c n t 1 cnt_1 c n t 1 ,染成颜色 2 2 2 的是 c n t 2 cnt_2 c n t 2 。用 bitset
记录哪几个完全子图大小能凑出来(因为 bitset
常数小到爆),然后枚举每一个 i ∈ [ 0 , n ] i \in [0,n] i ∈ [ 0 , n ] 记录最大的 a n s ans an s ,这题就做完了。
今天是 wgc
的 15 15 15 岁生日,让我们祝他生日快乐!!
P3959 [NOIP2017 提高组] 宝藏 solution 我们看这个:要让访问所有宝藏屋的代价和最小,那不就是最小生成树吗?但是 n n n 如此小,肯定有鬼。一看:代价不直接是边长 L L L ,而是 K × L K \times L K × L 。这里还多了一个 K K K ,所以贪心不对。因为有这个 K K K ,我们考虑把这个生成数分成 k k k 层。我们设 d p k , i dp_{k,i} d p k , i 为访问到了第 k k k 层,总共访问的状态为 i i i 的最小代价。那么我们肯定要从 i i i 的子集转移而来。我们枚举每一个子集,并枚举子集里的点,找出它到补集中的任意一个点的最短路,然后把子集里的每一个点到补集的最短路求和,就可以得到答案。形式化讲:d p k , i = min j ⊂ i ( d p k − 1 , j + ∑ u ∉ j , u ∈ ∁ i j w u , j ) dp_{k,i} = \min\limits_{j \subset i}(dp_{k - 1,j} + \sum\limits_{u \not \in j,u\in \complement_{i}j}w_{u,j}) d p k , i = j ⊂ i min ( d p k − 1 , j + u ∈ j , u ∈ ∁ i j ∑ w u , j ) ,其中,w u , j w_{u,j} w u , j 表示子集 j j j 里的点 u u u 到补集 ∁ i j \complement_{i}j ∁ i j 里的任意一个点的最短路,也就是 min v ∈ ∁ i j ( d i s u , v ) \min\limits_{v \in \complement_{i}j}(dis_{u,v}) v ∈ ∁ i j min ( d i s u , v ) 。初始化会在代码里说明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 ));
}
}