UVA 10318 Security Panel(DFS剪枝 + 狀壓 + 思維)題解
阿新 • • 發佈:2019-02-20
mat 題意 我們 %s 初始 max 地方 ont change
題意:給一個r*c的矩陣開關(初始全打開的),每次按下一個開關都會改變3*3範圍內的有*的地方的狀態,問你最少幾步能讓開關全閉上,按升序輸出按哪些按鈕
思路:每個按鈕至多按一下,按按鈕的順序和結果無關。我們把當前矩陣的開關狀態狀壓到 long long,並且預處理按下每個按鈕的變化,這樣可以直接異或得到新的狀態。這裏還需要剪枝,因為順序無關,我們直接從1按到r*c,那麽如果我們按到第k行,現在就已經影響不到k-2行了,所以k-2行如果有開關沒閉上就return。
代碼:
#include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<string> #include<cstdio> #include<cstring> #include<sstream> #include<iostream> #include<algorithm> typedef long long ll; using namespace std; const int maxn = 1e5 + 10; const int MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; int r, c; char s[10][10]; ll change[30], ansStep[30], step[30], vis[10][10], lit, ansCnt, cnt, OK; void init(int x, int y){ int pos = (x - 1) * c + y; change[pos] = 0; for(int i = 1; i <= 3; i++){ for(int j = 1; j <= 3; j++){ if(s[i][j] == ‘.‘) continue; int xx = x + i - 2, yy = y + j - 2; if(xx < 1 || xx > r || yy < 1 || yy > c) continue; ll p = (xx - 1) * c + yy; change[pos] += (1LL << p); } } } void dfs(int pos){ if(pos > r * c) return; int x = pos / c, y = pos % c; if(x >= 3){ for(int i = 1; i <= c; i++){ int pos2 = (x - 3) * c + i; if(!(lit & (1LL << pos2))) return; } } lit ^= change[pos]; vis[x][y] = 1; step[cnt++] = pos; if(lit == OK){ if(cnt < ansCnt){ for(int i = 0; i < cnt; i++) ansStep[i] = step[i]; ansCnt = cnt; lit ^= change[pos]; vis[x][y] = 0; cnt--; return; } } dfs(pos + 1); lit ^= change[pos]; vis[x][y] = 0; cnt--; dfs(pos + 1); } int main(){ int ca = 1; while(~scanf("%d%d", &r, &c) && r + c){ OK = 0; for(int i = 1; i <= 3; i++) scanf("%s", s[i] + 1); for(int i = 1; i <= r; i++){ for(int j = 1; j <= c; j++){ init(i, j); OK += (1LL << ((i - 1) * c + j)); } } ansCnt = 100; memset(vis, 0, sizeof(vis)); dfs(1); printf("Case #%d\n", ca++); if(ansCnt == 100) printf("Impossible.\n"); else{ for(int i = 0; i < ansCnt; i++){ if(i != 0) printf(" "); printf("%d", ansStep[i]); } printf("\n"); } } return 0; }
UVA 10318 Security Panel(DFS剪枝 + 狀壓 + 思維)題解