UVa 322 - Ships

題目:打船(我們以前是畫飛機的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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值