一开始在搞什么贪心,其实这个数据量就应该是搜索。先确定中心点的位置有至多49个,而其中状态不能确定的只有25个。在搜索到已经被覆盖的状态时直接往下一步走就可以了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n, m; char g[15][15]; char vis[15][15]; struct Cross { int x, y; int p; bool operator<(const Cross &c)const { return p < c.p; } } c[60]; int ctop; inline void update(const int &i, const int &j, int &rest) { ++vis[i][j]; if(vis[i][j] == 1) { rest -= 1; } } inline void unupdate(const int &i, const int &j, int &rest) { --vis[i][j]; if(vis[i][j] == 0) { rest += 1; } } inline void Update(const int &i, const int &j, int &rest) { update(i, j, rest); update(i + 1, j, rest); update(i, j + 1, rest); update(i - 1, j, rest); update(i, j - 1, rest); } inline void UnUpdate(const int &i, const int &j, int &rest) { unupdate(i, j, rest); unupdate(i + 1, j, rest); unupdate(i, j + 1, rest); unupdate(i - 1, j, rest); unupdate(i, j - 1, rest); } int ans; inline void add_cross(int i, int j) { if(g[i][j] != '#' || g[i - 1][j] != '#' || g[i][j - 1] != '#' || g[i + 1][j] != '#' || g[i][j + 1] != '#') return; ++ans; ++vis[i][j]; ++vis[i - 1][j]; ++vis[i][j - 1]; ++vis[i + 1][j]; ++vis[i][j + 1]; ++ctop; c[ctop].x = i; c[ctop].y = j; c[ctop].p = min(min(i - 1, j - 1), min(n - i, m - j)); } void dfs(int id, int cur, int rest) { /*printf("cur=%d\n", cur); for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { if(g[i][j] == '.') printf("%c", '.'); else { if(vis[i][j]) { printf("%c", '*'); } else { printf("%c", '#'); } } } puts(""); } puts("");*/ if(rest == 0) { ans = min(ans, cur); return; } //最优性剪枝+可行性剪枝 if(cur + (rest + 4) / 5 >= ans) return; int x = c[id].x, y = c[id].y; if(id == ctop) { ++cur; Update(x, y, rest); if(rest == 0) ans = min(ans, cur); UnUpdate(x, y, rest); --cur; return; } int cntempty = 0; cntempty += (vis[x][y] == 0); cntempty += (vis[x - 1][y] == 0); cntempty += (vis[x][y - 1] == 0); cntempty += (vis[x + 1][y] == 0); cntempty += (vis[x][y + 1] == 0); //优化2:根据没有覆盖的格子数进行判断优先搜索左子树还是右子树,这个值低则更容易覆盖满并更新答案,但也有可能浪费一步 if(cntempty >= 2) { ++cur; Update(x, y, rest); dfs(id + 1, cur, rest); UnUpdate(x, y, rest); --cur; dfs(id + 1, cur, rest); } else { dfs(id + 1, cur, rest); ++cur; Update(x, y, rest); dfs(id + 1, cur, rest); UnUpdate(x, y, rest); --cur; } return; } int main() { #ifdef Yinku freopen("Yinku.in", "r", stdin); #endif // Yinku int T; scanf("%d", &T); for(int ti = 1; ti <= T; ++ti) { memset(vis, 0, sizeof(vis)); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) { scanf("%s", g[i] + 1); } int rest = 0; for(int i = 1; i <= n ; ++i) { for(int j = 1; j <= m ; ++j) { if(g[i][j] == '#') { ++rest; } } } ans = 0, ctop = 0; for(int i = 2; i <= n - 1; ++i) { for(int j = 2; j <= m - 1; ++j) { if(g[i][j] == '#') { add_cross(i, j); } } } bool suc = true; for(int i = 1; i <= n ; ++i) { for(int j = 1; j <= m ; ++j) { if(g[i][j] == '#' && vis[i][j] == 0) { suc = false; break; } } if(!suc) break; } printf("Image #%d: ", ti); if(!suc) { puts("impossible"); } else { memset(vis, 0, sizeof(vis)); //最外圈的是必选的,可以立刻剪掉,搭配优化2可以将搜索的规模限制在5*5内 int cur = 0; for(int j = 1; j <= m ; ++j) { if(g[1][j] == '#' && vis[1][j] == 0) { ++cur; Update(2, j, rest); } if(g[n][j] == '#' && vis[n][j] == 0) { ++cur; Update(n - 1, j, rest); } } for(int i = 1; i <= n ; ++i) { if(g[i][1] == '#' && vis[i][1] == 0) { ++cur; Update(i, 2, rest); } if(g[i][m] == '#' && vis[i][m] == 0) { ++cur; Update(i, m - 1, rest); } } //打乱相邻节点的顺序,可能是负优化因为相邻节点或许可以优先走不印 //random_shuffle(c + 1, c + 1 + ctop); sort(c + 1, c + 1 + ctop); dfs(1, cur, rest); printf("%d\n", ans); } puts(""); } }