UVA 322 ships (POJ 1138)

题目地址:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=258

http://poj.org/problem?id=1138

题目描写叙述:

 Ships 

Probably everyone who ever attended school knows the game where two opposing players place a set of ships on a sheet of paper and try to eliminate each other's ships by guessing their location.

In our version of the game, your opponent has distributed the following seven ship patterns over a rectangular grid of squares:

                      xx   xx     xx   x       x    x
                      xx    xx   xx    xxx   xxx   xxx   xxxx

Each ship pattern covers exactly four squares. The patterns may be rotated but not mirrored. All patterns are guaranteed to be placed completely within the boundaries of the rectangle and not to overlap each other, whereas touching another pattern or the border is allowed.

We assume that we are in the middle of the game and that several squares have already been uncovered. You will be given a rectangular grid of squares representing your current knowledge about the positions of your enemy's ships. Every square is marked by one of the following characters:

  • `x' if a ship covers the square
  • `o' if no ship covers the square
  • `.' if the square has not yet been uncovered

Given that information, you are to decide whether you can determine all remaining `x' squares with at most one miss, i.e. whether you could uncover the `.' squares without getting more than one `o' square before you had all `x' squares uncovered. This means you are allowed to hit a `o' if then the solution becomes unique.

Input

The input file contains several game situations. Every test case starts with a line containing two integers w and h. These define width and height of the game rectangle, where tex2html_wrap_inline46 .

Each of the next h lines contains a string of w characters. Each of these characters is either `x'`o' or `.', depending on the state of the corresponding square.

A blank line separates each game from the next. The input file ends with a game having w = 0 and h = 0. This game should not be processed.

Output

For each test case you should first output a line containing the number of the game, followed by a line containing either `yes.' (if you can determine all `x' with at most one miss) or `no.' (if you cannot determine all `x' without at least two misses).

Output a blank line after every game.

Sample Input

10 10
.x..x.....
oooooxoooo
oxooxxx...
xxoooooo..
xoooxooo..
ooxxxxoo..
oooooxxoox
ooooooxoox
ooooooooxx
oooooooooo

0 0

Sample Output

Game #1
yes.

题意:

电脑最多仅仅有一次失败的机会(或者0次失败)来打中(揭开)全部的‘x’。


题解:

这题卡了我非常久,今天最终AC了。
分析:
事实上这题主要了两个基本的问题须要解决。


1、得到输入的图形。怎样得到全部完备的7船模型???这里的7船模型指的是。我们揭开全部的7仅仅船的全部模式在图中。即图中的方格仅仅能是‘o’或者‘x’。没有不确定的'.'了,模型有7种。模式是一种模型通过不同的旋转变化而来。


     事实上这里我们可以用DFS 7种模型的全部模式在图中尝试匹配,遍历第一种模型中的全部模式,假设可以匹配,那我们DFS下一种模型;假设匹配到第7种模型,而且能匹配成功,那么我们就得到了当中一种可能的完备7船模型图(有非常多中可能的7船模型图),把他保存下来,以便后面的步骤用来确定是yes还是no的终于答案。


2、在得到了全部可能的7船模型后,我们怎样通过这些可能的7船模型来得出终于的yes还是no呢??
    事实上这里我先以题中的例子分析过程来展示详细是怎样得出yes和no的:
首选,例子的形式例如以下所看到的:
.x..x.....
oooooxoooo
oxooxxx...
xxoooooo..
xoooxooo..
ooxxxxoo..
oooooxxoox
ooooooxoox
ooooooooxx
oooooooooo


这个我们不难发现这样一个输入图。一共能得出3种可能的7船完备图(自己在纸上画画就可以):


oxxxxooooo
oooooxoooo
oxooxxxooo
xxoooooooo
xoooxoooxx
ooxxxxooxx
oooooxxoox
ooooooxoox
ooooooooxx
oooooooooo


oxooxooooo
oooooxoooo
oxooxxxooo
xxooooooxx
xoooxoooxx
ooxxxxoooo
oooooxxoox
ooooooxoox
ooooooooxx
oooooooooo


oxxxxooooo
oooooxoooo
oxooxxxoxx
xxooooooxx
xoooxooooo
ooxxxxoooo
oooooxxoox
ooooooxoox
ooooooooxx
oooooooooo


这样我们得到了上述3种的完备图形。即不管输入图形怎么变化。仅仅可能是这3种可能。那么电脑是怎样通过分析这3种可能的7船完备图来得出yes还是no呢??
通过细致观察我们不难发现,综合这3种船模型,我们发现大部分点在3种模型中的点是固定不变的,而有一部分点是在3种模型的点是变化的,就可以能在一种模型中是'x'而在还有一种模型中是'o'。显然电脑能得出yes和no,肯定来自于这些变化的点。我最好还是把变化的点提出来。这里例子中变化的点有8个:
..
..
..
分别有下面可能的分布:
oo
oo
xx
xx


oo
xx
xx
oo


xx
xx
oo
oo


那么o我们来看电脑怎么据此得出yes或者no的。首选电脑选择8个点中间的4个点的随意一点揭开。这里我们以中间4个点的坐下点为例。相对于8个点的坐标是(2,0)
1、假设这个点是'o',那么显然我们能够排除第一种模型和另外一种模型,显然就仅仅有第三种模型,所以电脑这里仅失误了一次就能确定是哪个模型了。


2、假设这个点是‘x’,那么我们能够把第三种模型排除掉。因为我们另一次打偏的机会(揭开的点为'o'),所以o我们继续取。这时候我们取最后一行的两点的随意点。假设是'x'那么肯定是第一种,电脑没有失误一次得到一个确定模型;假设是'o',那么肯定是另外一种,那么电脑也仅失误一次得到一种确定的模型图。


综上。电脑都能符合要求(仅失误一次或0次)得到确定的全部可能的7船模型。所以是yes;反之假设在当中 一个过程中电脑不能获得,那就直接是no了。
这里我们肯定在想,电脑是怎样确定取哪个点来作为'x'或者'o'。来分析的呢,就如上面,为什么电脑一開始一定要选择中间的4个点呢??
事实上这里我们能够通过这些点在全部可能的7船模型中出现'x'的数量和出现'o'的数量来区分中间4个点和两端的4个点:
中间4点->1*o,2*x    两端4点->2*o,1*x
即中间4点在全部可能模型中出现1次'o',出现2次'x'。
这样电脑确定yes和no的策略为:每次选取出现o最少次数的点作为分析点,为'x'分析下去。为'o'分析下去;假设全部的分析,电脑仅在失误一次的情况下,剩下的完备7船模型都是小于等于1的,那么表示电脑可以确定,为yes。反之就为no。


数据结构:
由于这些非固定的点实在太重要,我们最好还是对这些点做一个定义,这里我定义非固定点为歧义点,反之则是非歧义点。


我们定义:在图形中的某些点,他们在全部可能的7船模型中会因不同的模型,其值('x' or  'o')也会不同。我们称这些点为歧义点,反之为非歧义点。
歧义点/非歧义点的数据结构:
         int m;(表示在全部可能的7船模型中出现'o'的次数)
         int n;(表示在全部可能中出现'n'的次数)
显然m>0&&n>0为歧义点。反之则是非歧义点。
算法:
一、通过DFS得到全部的可能的完备7船模型:
1、DFS一种模型
2、遍历一种模型的全部模式。假设匹配
3、DFS下一种模型
4、假设匹配到7中模型都能匹配,发现一种可能的完备7船模型,保存。
二、得到全部可能的7船模型,构造歧义点/非歧义点:
1、遍历全部的7船模型
2、遍历一个可能的7船模型的全部方格
3、构造歧义点/非歧义点,假设是'x'则n++,假设是'o'则m++
三、DFS歧义点/非歧义点表,确定终于的yes或者no
1、选择最小的m的歧义点(注意一定要是歧义点)作为DFS点。
2、假设此点是'o'。剔除这个点是'x'的7船模型(而且更新歧义点/非歧义点信息)。然后DFS下一个点。


3、假设此点是'x'。剔除这个点是'o'的7船模型(而且更新歧义点/非歧义点信息),然后DFS下一个点。
4、假设仅用了一次‘o’或者0次'o'作为DFS点,而且可能的7船模型数量小于等于1(即表示电脑能够确定了,或者全部点都是非歧义点了),那么表示电脑在这个分支能够确定7船模型。
5、假设电脑在全部分支都能这样确定,则输出yes。反之输出no(即仅仅要有一个分支不能确定。就全盘都是no)
剪枝:
1、'x'的数量一定是28=4*7。(7中模型,每一个模型肯定占4个'x'。在遍历中记录'x'的数量,一旦超过28,直接回退这个分支,不是必需再深搜下去)
2、7船完备模型一定要是每种模式仅出现一次,且必须出现一次,不能反复,也不能缺漏。
3、假设得到的可能的7船模型图的数量大于h*w。那么直接输出no。(这里我们假设。就算我们以h*w方阵的每一个点作为一次miss(即该点为'o'),最多能得到h*w个不同的7船完备模型图。假设我们得到的全部可能的7船模型图数量大于了h*w;那么必定存在一个点或多个点,我们以此作为miss,作为失误的'o',肯定相应两种及以上的7船完备模型,这样肯定就不能仅满足一次失误的条件,所以肯定是no。


注意:
1、很多其它具体设计见代码。
2、这里UVA的输入格式与POJ的输入格式是不同的,当中
UVA的是:w h(先输入w再输入h)  POJ的是:h w(先输入h再输入w) 不然会得到WA
3、我这里的代码是UVA的版本号(即先w再h),假设要改成POJ的,仅仅须要在输出的地方把我代码的H和W调换一下即可了。


代码:

#include<stdio.h>
#include<string.h>
#define LL 20
#define X 28 //x sum count = 7*4
typedef struct pot
{
    int m;//the number of the 'o'
    int n;//the number of the 'x'
    bool is_various;//if m > 0 and n > 0 then is_various = true
    int x;//the coordinate of x,(0,0) is origin point
    int y;//the coordinate of y,(0,0) is origin point
    pot()
    {
        m=0;
        n=0;
        is_various=false;
        x=0;
        y=0;
    }
}pot,*pot_link;
typedef struct grid
{
    int r;
    int c;
    pot val[LL][LL];
    grid()
    {
        r=0;
        c=0;
    }
}grid,*grid_link;
char grids[500][LL][LL];//save the possible complete grid (no '.' only 'o' or 'x')
int cnt=0;//count of the grids
int used_grids[500]={0};//0 is not visited  x>0 is cur==x  we visited it
char in_grid[LL][LL];//input grid
grid pot_grid;//save the cases of pot in grid
int W=0,H=0;//the input grid 's width and height
int R=0,C=0;//the input of row and col
int cases=0;
int x_cnt=0;//x count of input
int o_cnt=0;//o count of input
int i_cnt=0;//. count of input and x_cnt + o_cnt + i_cnt == W * H
//the 7 kinds of boat all patterns include per kind and rotate 4 times
char patterns[19][10][10]=
{
  {
      "xx",//1 kind -> 1 pattern
      "xx"
  },
  {
      "xx",//1 kind-> 2 patterns
      " xx"
  },
  {
      " x",
      "xx",
      "x"
  },
  {
      " xx",//1 kind -> 2 patterns
      "xx"
  },
  {
      "x ",
      "xx",
      " x"
  },
  {
      "x",//1 kind -> 4 patterns
      "xxx"
  },
  {
      "xx",
      "x",
      "x"
  },
  {
      "xxx",
      "  x"
  },
  {
      " x",
      " x",
      "xx"
  },
  {
      " x ",//1 kind -> 4 patterns
      "xxx"
  },
  {
      "x",
      "xx",
      "x"
  },
  {
      "xxx",
      " x"
  },
  {
      " x",
      "xx",
      " x"
  },
  {
      "  x",//1 kind -> 4 patterns
      "xxx"
  },
  {
      "x",
      "x",
      "xx"
  },
  {
      "xxx",
      "x"
  },
  {
      "xx",
      " x",
      " x"
  },
  {
      "xxxx"//1 kind -> 2 patterns
  },
  {
      "x",
      "x",
      "x",
      "x"
  }
};
int kind_cnt[7]={1,2,2,4,4,4,2};//kind count of the 7 kind of boats, i(begin with 0) kind jth pattern is patterns[kind_cnt[0]+kind_cnt[1]+......+kind_cnt[i-1]+j]
int kind_ind[7]={0,1,3,5,9,13,17};//kind index in the patterns array
//in_grid  deal -> ('z','x'->'x'  'o','.'->'o')
int insert_grid()
{
    //insert the in_grid to grids
    for(int i=0;i<=R-1;i++)
    {
        for(int j=0;j<=C-1;j++)
        {
            if(in_grid[i][j]=='.') {grids[cnt][i][j]='o';}
            else if(in_grid[i][j]>=0&&in_grid[i][j]<=6) {grids[cnt][i][j]='x';}
            else {grids[cnt][i][j]=in_grid[i][j];}
        }
    }
    cnt++;
    if(cnt>=1000) {cnt--;}//exceed the boundary
    return(0);
}
//place next patterns and insert the complete grid into the grids
int DFS_kind(int cur)
{
    if(x_cnt>28) {return(0);}
    if(cnt>R*C) {return(-1);}
    if(cur>=7)
    {
        int x_c=0;
        for(int i=0;i<=R-1;i++)
        {
            for(int j=0;j<=C-1;j++)
            {
                if(in_grid[i][j]=='x'||(in_grid[i][j]>=0&&in_grid[i][j]<=6)) {x_c++;}
            }
        }
        if(x_c==28)//4(point) * 7(boat pattern)
        {
            insert_grid();//in_grid -> ('z','x'->'x'  'o','.'->'o') and not insert the repeat gird
        }
    }
    else
    {
        //scan this kind of all specific patterns
        for(int k=0;k<=kind_cnt[cur]-1;k++)
        {
            int pat_ind=kind_ind[cur]+k;
            //translation the pattern's origin point from (0,0) .... to ... (R-1,C-1)
            for(int nexti=0;nexti<=R-1;nexti++)
            {
                for(int nextj=0;nextj<=C-1;nextj++)
                {
                    //scan the patterns and in_grid by nexti,nextj
                    int i=0,j=0;
                    int match_cnt=0;
                    //record the previous value to reverse
                    int pre_x[5]={0};
                    int pre_y[5]={0};
                    char pre_char[5]={0};
                    while(i<=4-1&&j<=4-1)
                    {
                        char ch=patterns[pat_ind][i][j];
                        if(ch!='\0')
                        {
                            if(ch=='x')
                            {
                                if(nexti+i>R-1||nextj+j>C-1) {break;}//exceed boundary,it must no be matched
                                if(in_grid[nexti+i][nextj+j]=='x'||in_grid[nexti+i][nextj+j]=='.')//matched
                                {
                                    if(in_grid[nexti+i][nextj+j]=='.') {x_cnt++;}
                                    pre_x[match_cnt]=nexti+i;
                                    pre_y[match_cnt]=nextj+j;
                                    pre_char[match_cnt]=in_grid[nexti+i][nextj+j];
                                    match_cnt++;
                                    in_grid[nexti+i][nextj+j]=cur;//the current cur turn we mark it to indicate that this point have been visited at curth turn
                                }
                                else {break;}//the in_grid[nexti+i][nextj+j]=='o' or 'z',not matched
                            }
                        }
                        j++;
                        if(j>4-1)
                        {
                            j=0;
                            i++;
                        }
                    }
                    if(match_cnt==4)//is matched this kind dfs next kind
                    {
                        int flag=DFS_kind(cur+1);
                        if(flag==-1) {return(-1);}
                    }
                    //reverse the z -> x or .
                    for(int ci=0;ci<=match_cnt-1;ci++) {in_grid[pre_x[ci]][pre_y[ci]]=pre_char[ci];if(pre_char[ci]=='.') {x_cnt--;}}
                }
            }
        }
    }
    return(0);
}
//construct the pot_grid
int constr_pot()
{
    //construct the pot_grid
    for(int c=0;c<=cnt-1;c++)
    {
        for(int i=0;i<=R-1;i++)
        {
            for(int j=0;j<=C-1;j++)
            {
                if(grids[c][i][j]=='x') {pot_grid.val[i][j].n++;}
                else {pot_grid.val[i][j].m++;}
            }
        }
    }
    //construct the is_various
    for(int i=0;i<=R-1;i++)
    {
        for(int j=0;j<=C-1;j++)
        {
            if(pot_grid.val[i][j].m>0&&pot_grid.val[i][j].n>0) {pot_grid.val[i][j].is_various=true;}
        }
    }
    return(0);
}
//sub the char c in the all min_m.x min_m.y in the grids
int sub_grids(pot min_m,char ch,int cur)
{
    for(int c=0;c<=cnt-1;c++)
    {
        if(!used_grids[c]&&grids[c][min_m.x][min_m.y]==ch)
        {
            used_grids[c]=cur;
            for(int i=0;i<=R-1;i++)
            {
                for(int j=0;j<=C-1;j++)
                {
                    if(grids[c][i][j]=='x') {pot_grid.val[i][j].n--;}
                    else {pot_grid.val[i][j].m--;}
                    if(!(pot_grid.val[i][j].m>0&&pot_grid.val[i][j].n>0)) {pot_grid.val[i][j].is_various=false;}
                }
            }
        }
    }
    return(0);
}
//add the char c in the used_grids[x] == cur in the pot_grid,reverse the sub
int add_grids(int cur)
{
    for(int c=0;c<=cnt-1;c++)
    {
        if(used_grids[c]==cur)
        {
            used_grids[c]=0;
            for(int i=0;i<=R-1;i++)
            {
                for(int j=0;j<=C-1;j++)
                {
                    if(grids[c][i][j]=='x') {pot_grid.val[i][j].n++;}
                    else {pot_grid.val[i][j].m++;}
                    if(pot_grid.val[i][j].m>0&&pot_grid.val[i][j].n>0) {pot_grid.val[i][j].is_various=true;}
                }
            }
        }
    }
    return(0);
}
//dfs  the pot_grid and eliminate the various pot,the use_o must <= 1 , cur is the count of turn it must > 0
int DFS_pot(int cur,int use_o)
{
    if(use_o>=2) {return(0);}//have used 'o' twice or more times then it is false
    //find various pot , minimum m
    pot min_m;
    int flag=0;
    int mark=0;
    min_m.m=1000;
    for(int i=0;i<=R-1;i++)
    {
        for(int j=0;j<=C-1;j++)
        {
            if(pot_grid.val[i][j].is_various)
            {
                flag=1;
                if(pot_grid.val[i][j].m<min_m.m)
                {
                    min_m.m=pot_grid.val[i][j].m;
                    min_m.n=pot_grid.val[i][j].n;
                    min_m.x=i;
                    min_m.y=j;
                }
            }
        }
    }
    if(!flag) {return(1);}//no 'o' we direct get yes
    //suppose the min_m pot is 'o' so we eliminate the 'x' of grids
    sub_grids(min_m,'x',cur);
    //dfs
    mark=DFS_pot(cur+1,use_o+1);
    if(!mark) {return(0);}//if false then all false
    //reverse the  suppose of the min_m pot is 'o'
    add_grids(cur);
    //suppose the min_m pot is 'x'
    sub_grids(min_m,'o',cur);
    //dfs
    mark=DFS_pot(cur+1,use_o);
    if(!mark) {return(0);}//if false then all false
    //reverse the  suppose of the min_m pot is 'x'
    add_grids(cur);
    return(1);
}
int main()
{
    //note!! this problem in uva(322) is input is W H  but in poj(1138) is H W, current version is for uva
    while(scanf("%d%d",&W,&H)!=EOF&&(W+H)>0)
    {
        R=H;
        C=W;
        cnt=0;
        x_cnt=o_cnt=i_cnt=0;
        pot_grid.r=R;
        pot_grid.c=C;
        memset(grids,'\0',sizeof(grids));
        memset(used_grids,0,sizeof(used_grids));
        for(int i=0;i<=R-1;i++)//initialize the pot_grid
        {
            for(int j=0;j<=C-1;j++)
            {
                pot_grid.val[i][j].is_various=false;
                pot_grid.val[i][j].m=pot_grid.val[i][j].n=pot_grid.val[i][j].x=pot_grid.val[i][j].y=0;
            }
        }
        printf("Game #%d\n",++cases);
        for(int i=0;i<=R-1;i++)
        {
            scanf("%s",in_grid[i]);
            for(int j=0;j<=C-1;j++)
            {
                switch(in_grid[i][j])
                {
                case 'x':
                    x_cnt++;
                    break;
                case 'o':
                    o_cnt++;
                    break;
                case '.':
                    i_cnt++;
                    break;
                }
            }
        }
        //prune
        if(x_cnt>28)
        {
            printf("no.\n\n");
            continue;
        }
        //dfs the input grid
        DFS_kind(0);//place next patterns and insert the complete grid into the grids
        //construction pot_grid
        constr_pot();
        //dfs the pot_grid and get the  yes or no
        if(cnt>0&&cnt<=R*C&&DFS_pot(1,0)) printf("yes.\n\n");
        else printf("no.\n\n");
    }
    return(0);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值