思路
因为木板可以重叠,所以如果放置木板,木板两端肯定都顶到底(障碍或者边界)是最优的.
这样就可以预处理出一些横向与纵向的联通块(宽度都为1).因为每一个方格必须被覆盖,所以该方格隶属的横向联通块与纵向联通块至少有一个被木板覆盖.
这样就可以构建二分图,横向联通块为左部图,纵向联通块为右部图,每一个方格都是一条边,跑二分图最小点覆盖即可.
代码
#include<bits/stdc++.h> using namespace std; #define i64 long long #define fp( i, b, e ) for ( int i(b), I(e); i <= I; ++i ) #define fd( i, b, e ) for ( int i(b), I(e); i >= I; --i ) #define go( i, b ) for ( int i(b), v(to[i]); i; v = to[i = nxt[i]] ) template<typename T> inline void cmax( T &x, T y ){ x < y ? x = y : x; } template<typename T> inline void cmin( T &x, T y ){ y < x ? x = y : x; } clock_t t_bg, t_ed; const int MAXN = 55, MAXV = 2505, MAXE = 2505; int N, M; char s[MAXN][MAXN]; int b1[MAXN][MAXN], b2[MAXN][MAXN], n, m; int hd[MAXV], nxt[MAXE], to[MAXE], tot; int mc[MAXV]; bool vis[MAXV]; inline void addedge( int x, int y ){ nxt[++tot] = hd[x], hd[x] = tot, to[tot] = y; } bool DFS( int u ){ go( i, hd[u] ) if ( !vis[v] ){ vis[v] = 1; if ( !mc[v] || DFS(mc[v]) ) return mc[v] = u, 1; } return 0; } int main(){ t_bg = clock(); scanf( "%d%d", &N, &M ); fp( i, 1, N ) scanf( "%s", s[i] + 1 ); fp( i, 1, N ) fp( j, 1, M ) if ( s[i][j] == '*' ){ if ( s[i][j - 1] == '*' ) b1[i][j] = b1[i][j - 1]; else b1[i][j] = ++n; if ( s[i - 1][j] == '*' ) b2[i][j] = b2[i - 1][j]; else b2[i][j] = ++m; addedge( b1[i][j], b2[i][j] ); } int ans(0); fp( i, 1, n ) memset( vis, 0, sizeof vis ), ans += DFS(i); printf( "%d\n", ans ); t_ed = clock(); fprintf( stderr, "\n========info========\ntime : %.3f\n====================\n", (double)( t_ed - t_bg ) / CLOCKS_PER_SEC ); return 0; }