题解:P7171 [COCI 2020_2021 #3] Selotejp
posted on 2025-02-20 03:04:26 | under | source
闲话:为啥题解区清一色的轮廓线 dp?感觉怪怪的捏。
注意题意:一个格子必须恰被覆盖一次。
直接记 \(f_{i,S}\) 表示考虑前 \(i\) 行,其中第 \(i\) 行中 \(S\) 位置集合上有列方向的贴纸。
转移分三步走,一是继承上一行的贴纸,也就是超集取 \(\min\),子集卷积(俗称 sos dp)即可。然后是在本行新增一些列贴纸,枚举下一个新增位置即可。最后是本行使用行贴纸贴上空位,即连续段个数。
\(O(nm2^m)\)。
代码
把字符翻转了看的舒服点。
#include<bits/stdc++.h>
using namespace std;
#define MIN(a, b) a = min(a, b)
const int M = 1e3 + 5, N = 15;
int n, m, f[1 << N], g[1 << N], h[1 << N], sit[M], val[M][1 << N], inf;
char c[N];
signed main(){
scanf("%d%d", &n, &m);
for(int i = 0; i < n; ++i){
scanf("%s", c);
for(int j = 0; j < m; ++j) c[j] = (c[j] == '#' ? '.' : '#');
for(int j = 0; j < m; ++j) if(c[j] == '#') sit[i] += (1 << j);
for(int S = 0; S < 1 << m; ++S)
for(int j = 0; j < m; ++j)
if((c[j] == '.' && (!((S >> j) & 1))) && (j == 0 || (c[j - 1] == '#' || ((S >> (j - 1)) & 1)))) ++val[i][S];
}
memset(f, 0x3f, sizeof f), inf = f[0];
f[0] = 0;
for(int i = 0; i < n; ++i){
for(int S = 0; S < 1 << m; ++S) g[((1 << m) - 1) ^ S] = f[S];
memset(f, 0x3f, sizeof f);
for(int i = 0; i < m; ++i)
for(int S = 0; S < 1 << m; ++S)
if((S >> i) & 1) MIN(g[S], g[S ^ (1 << i)]);
for(int S = 0; S < 1 << m; ++S)
f[S] = g[((1 << m) - 1) ^ S];
for(int S = 0; S < 1 << m; ++S)
for(int j = 0; j < m; ++j)
if(!((S >> j) & 1))
MIN(f[S | (1 << j)], f[S] + 1);
for(int S = 0; S < 1 << m; ++S)
if(!(S & sit[i])) f[S] += val[i][S];
else f[S] = inf;
}
int ans = inf;
for(int S = 0; S < 1 << m; ++S) MIN(ans, f[S]);
cout << ans;
return 0;
}

浙公网安备 33010602011771号