题解: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;
}
posted @ 2026-01-15 08:19  Zwi  阅读(2)  评论(0)    收藏  举报