題目:打船(我們以前是畫飛機的o(╯□╰)o。。。),有一個二維的方格平面,有7只船(如下圖所示),
可以在平面內旋轉;給出一張進行到一半的圖形,裡面有空和船的部分信息,問能否最多失誤一次,
確定所有船的位置。
分析:圖論、搜索。很明顯需要搜索。失誤一次最開始沒理解,這個是解體的關鍵。
最多失誤一次的意思是,如果輸出所有'x'的格子的位置,最多有一個不是'x'。
上面的解釋貌似也不是很清楚,這裡根據解題過程解釋一下:
1.解的定義:我們不關心每隻船的具體位置和旋轉,我們只關心最後形成的xo圖形;
如果船的位置不同但是最後的圖形相同,認為是一組解(我們只關心是不是xo);
2.多解情況:如果是無解或者只有一個解,那麼可以確定答案;
如果有超過一個解怎麼確定?這就是失誤一次的關鍵;
現在有多組解,每組解都是一個xo構成的圖形,如果有一個圖形的(x,y)位置,
對應為o,其他解都是x,那麼就選擇這個位置(打這個位置);
(如果它真的是o,則一定是這個解,否則(x,y)位置就是x,我們沒有失誤)
因此,從剩下的解裡面再找到只有一個是o的位置(u,v)重複這兒過程;
如果按照這個方法排除掉所有的解(最後可能剩下一個),就是可解的,否則無解;
根據上面的過程,我們就可以確定解題的方法了:
1.求所有解:使用回溯法(用船作為搜索的深度變量),求解所有可行解,這裡需要設定搜索上界;
如果按照上面判斷多解的過程,每次可以排除一個解,那麼最最多可以排除'.'的個數;
(每次確定一個解,相當於對應位置的'.'設置為x)
2.判斷多解:每次找到對應位置只有一個o的解,刪除,循環這個過程;
最後剩下不超過1個,則可以失誤一次確定答案。
說明:寫了好久╮(╯▽╰)╭。
#include <stdio.h>
#include <stdlib.h>
char maps[16][17] = {0};
char ans[300][16][16];
int ans_count = 0;
int ship_rotation[7] = {1, 2, 2, 4, 4, 4, 2};
char ships[7][4][4][5] = {
"xx ", "xx ", " ", " ", // xx
" ", " ", " ", " ", // xx
" ", " ", " ", " ",
" ", " ", " ", " ",
"xx ", " xx ", " ", " ", // xx
" x ", "xx ", "x ", " ", // xx
" ", " ", " ", " ",
" ", " ", " ", " ",
" xx ", "xx ", " ", " ", // xx
"x ", "xx ", " x ", " ", // xx
" ", " ", " ", " ",
" ", " ", " ", " ",
"x ", "xxx ", " ", " ", // x
"xx ", "x ", "x ", " ", // xxx
"xxx ", " x ", " ", " ", //
" x ", " x ", "xx ", " ", //
" x ", "xxx ", " ", " ", // x
"xx ", " x ", " x ", " ", // xxx
"xxx ", "x ", " ", " ", //
"x ", "x ", "xx ", " ", //
" x ", "xxx ", " ", " ", // x
" x ", "xx ", " x ", " ", // xxx
"xxx ", " x ", " ", " ", //
"x ", "xx ", "x ", " ", //
"xxxx", " ", " ", " ", // xxxx
"x ", "x ", "x ", "x ", //
" ", " ", " ", " ",
" ", " ", " ", " ",
};
int match(int s, int t, int c, int r, int m, int n)
{
for (int i = 0; i < 4; ++ i)
for (int j = 0; j < 4; ++ j) {
if (ships[s][t][i][j] != 'x') {
continue;
}else if (c+i < 0 || c+i >= m || r+j < 0 || r+j >= n) {
return false;
}else if (maps[c+i][r+j] != '.' && maps[c+i][r+j] != 'x') {
return false;
}
}
return true;
}
char smap[7][16][17];
void dfs(int d, int h, int w, int dot)
{
if (ans_count > dot) { // 超過搜索上界
return;
}
if (d >= 7) {
for (int i = 0; i < h; ++ i)
for (int j = 0; j < w; ++ j) {
if (maps[i][j] == 'x') {
return;
}
if (maps[i][j] >= '0' && maps[i][j] <= '9') {
ans[ans_count][i][j] = 'x';
}else {
ans[ans_count][i][j] = ' ';
}
}
for (int p = 0; p < ans_count; ++ p) { // 去重
int flag = 0;
for (int i = 0; i < h; ++ i)
for (int j = 0; j < w; ++ j) {
if (ans[p][i][j] != ans[ans_count][i][j]) {
flag = 1;
break;
}
}
if (!flag) {
return;
}
}
ans_count ++;
return;
}
for (int x = 0; x < h; ++ x) // 地圖坐標
for (int y = 0; y < w; ++ y) {
for (int t = ship_rotation[d]-1; t >= 0; -- t) {
if (match(d, t, x, y, h, w)) {
for (int i = 0; i < 4; ++ i) // 標記
for (int j = 0; j < 4; ++ j) {
if (ships[d][t][i][j] == 'x') {
smap[d][x+i][y+j] = maps[x+i][y+j];
maps[x+i][y+j] = '0'+d;
}
}
dfs(d+1, h, w, dot);
for (int i = 0; i < 4; ++ i) // 逆標記
for (int j = 0; j < 4; ++ j) {
if (ships[d][t][i][j] == 'x') {
maps[x+i][y+j] = smap[d][x+i][y+j];
}
}
}
}
}
}
int determine(int h, int w, int dot)
{
ans_count = 0;
dfs(0, h, w, dot);
if (ans_count == 1) {
return 1;
}else if (ans_count > dot || !ans_count) {
return 0;
}
int flag = 1;
while (flag) {
flag = 0;
for (int x = 0; x < h; ++ x)
for (int y = 0; y < w; ++ y) {
if (maps[x][y] == '.') {
int count = 0;
for (int i = 0; i < ans_count; ++ i) {
if (ans[i][x][y] == ' ') {
count ++;
}
}
if (count == 1) {
int id = 0;
for (int i = 0; i < ans_count; ++ i) {
if (ans[i][x][y] == ' ') {
id = i;
break;
}
}
for (int p = 0; p < h; ++ p) // 刪除對應(x,y)有唯一o的解
for (int q = 0; q < w; ++ q) {
ans[id][p][q] = ans[ans_count-1][p][q];
}
ans_count --;
flag = 1;
}
}
}
}
return ans_count < 2;
}
int main()
{
int w, h, cases = 1;
while (~scanf("%d%d",&w,&h) && w+h) {
int dot_count = 0;
for (int i = 0; i < h; ++ i) {
scanf("%s",maps[i]);
for (int j = 0; j < w; ++ j) {
dot_count += (maps[i][j]=='.');
}
}
printf("Game #%d\n",cases ++);
if (determine(h, w, dot_count)) {
puts("yes.\n");
}else {
puts("no.\n");
}
}
return 0;
}