AOJ 0091 - Blur
3^n系だ.いい問題だと思います.
解法
全探索.
左上から右下に走査し,染められている場所を探す.
その場所を(x, y)とすると,そこを染めるには
(x, y+1)から「小」を垂らす
(x+1, y+1)から「中」を垂らす
(x, y+2)から「大」を垂らす
の3通りがある.これらをすべて試すのを繰り返す.
3つともダメだったらできない.
場所探しはO(WH),毎回3通りの染め方を試すので,計算量はO(3^N W H)(だと思います.間違ってたらご指摘ください.)
コード
塗ったり,塗れるかを試すときはマンハッタン距離をつかうといいかも.
#include <cstdio> #include <algorithm> #include <string> const int dx[3] = {0, 1, 0}, dy[3] = {1, 1, 2}; int N; int cloth[10][10]; int x[12], y[12], type[12]; bool canFill(int cx, int cy, int t){ int mx; if(t == 0){ mx = 1; }else if(t == 1){ mx = 2; }else if(t == 2){ mx = 2; } for(int i=-mx;i<=mx;i++){ for(int j=-mx;j<=mx;j++){ if(std::abs(i) + std::abs(j) > mx){continue;} if(t == 1 && (i == 0 && std::abs(j) == 2 || std::abs(i) == 2 && j == 0)){continue;} int x = j + cx, y = i + cy; if(x < 0 || x >= 10 || y < 0 || y >= 10){return false;} if(!cloth[y][x]){return false;} } } return true; } void spuit(int cx, int cy, int t, int v){ int mx; if(t == 0){ mx = 1; }else if(t == 1){ mx = 2; }else if(t == 2){ mx = 2; } for(int i=-mx;i<=mx;i++){ for(int j=-mx;j<=mx;j++){ if(std::abs(i) + std::abs(j) > mx){continue;} if(t == 1 && (i == 0 && std::abs(j) == 2 || std::abs(i) == 2 && j == 0)){continue;} int x = j + cx, y = i + cy; if(x < 0 || x >= 10 || y < 0 || y >= 10){continue;} cloth[y][x] -= v; } } } bool dfs(int n){ if(n == N){ for(int i=0;i<10;i++){ for(int j=0;j<10;j++){ if(cloth[i][j]){return false;} } } for(int i=0;i<N;i++){ printf("%d %d %d\n", x[i], y[i], type[i]); } return true; } for(int i=0;i<10;i++){ for(int j=0;j<10;j++){ if(!cloth[i][j]){continue;} for(int k=0;k<3;k++){ int cx = j + dx[k], cy = i + dy[k]; if(!canFill(cx, cy, k)){continue;} spuit(cx, cy, k, 1); x[n] = cx; y[n] = cy; type[n] = k + 1; if(dfs(n+1)){return true;} spuit(cx, cy, k, -1); } return false; } } return false; } int main(){ scanf("%d", &N); for(int i=0;i<10;i++){ for(int j=0;j<10;j++){ scanf("%d", &cloth[i][j]); } } dfs(0); }