roblem Description

The pollution of the earth is so serious that people can not survive any more. Fortunately, people have found a new planet that maybe has life, and we call it "Pandora Planet".

Leonardo Da Vinci is the only astronaut on the earth. He will be sent to the Pandora Planet to gather some plant specimens and go back. The plant specimen is important to the people to decide whether the planet is fit to live or not.

Assuming that Da Vinci can only move in an N×M grid. The positions of the plant specimens he wants to collect are all marked by the satellite. His task is to find a path to collect all the plant specimens and return to the spaceship. There are some savage beasts in the planet. Da Vinci can not investigate the grid with the savage beast. These grids are also marked by the satellite. In order to save time Da Vinci could only visit each grid exactly once and also return to the start grid, that is, you can not visit a grid twice except the start grid. You should note that you can choose any grid as the start grid.

Now he wants to know the number of different paths he can collect all the plant specimens. We only care about the path and ignore where the start grid is, so the two paths in Figure 1 are considered as the same.

<center> </center> <center> Figure 1 </center>

Input

The first line of the input contains an integer T (T≤100), indicating the number of cases. Each case begins with a line containing two integers N and M (1≤N, M≤12), the size of the planet is N×M. Each of the following N lines contains M characters Gij(1≤i≤N, 1≤j≤M), Gij denotes the status of the grid in row i and column j, where 'X' denotes the grid with savage beast, '*' denotes the safe grid that you can decide to go or not, 'O' denotes the plant specimen you should collect. We guarantee that there are at least three plant specimens in the map.

Output

For each test case, print a line containing the test case number (beginning with 1) and the number of different paths he can collect all the plant specimens. You can make sure that the answer will fit in a 64-bit signed integer.

Sample Input

22 2OOO*4 4***OXO****O*XX**

Sample Output

Case 1: 1Case 2: 7

Source

The 35th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest

一个多月没做忘得太狠了==多亏心虚复习一下

题意:三种格子:可选、必走、不能走,问单回路方法数。增加一个变量标记是否出现过回路,如果出现过,后面还有插头或者必走格子,这个状态作废,作废的写法是遍历进行下一次循环,而不是暴力的直接停止,毕竟 是dp,某种情况不合法不算他就得了!

而且值得注意的是,这个标记变量是戴在st状态的最高位的,因为对于有相同轮廓线的状态来说,可以分成已经形成回路和没有形成回路的,是两种情况。无论形成回路与否都有可能是合法的方法,毕竟有可选格子的存在。这也就能解释为什么isend不作为st的最高位时,第二种情况输出0,因为我武断的让因为有回路而后面(left||up||maze[i][j]==2)整体都pass掉了==其实没有回路的还可以继续走啊,不知道这么理解对不对==

/**********
fzu1997
2016.3.12
4220	375
GNU C++
	4995
2016-03-12 10:55:20
*********/
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;

const int MAXD=15;
const int HASH=10007;
const int STATE=100010;

int N,M;
int code[MAXD];
int maze[MAXD][MAXD];//0表示障碍点,1表示非必走点,2表示必走点
int ch[MAXD];
int isend;//是否形成回路标记位
struct HASHMAP
{
    int head[HASH],next[STATE],size;
    long long state[STATE];
    long long f[STATE];
    void init()
    {
        size=0;
        memset(head,-1,sizeof(head));//用单独链表法处理碰撞
    }
    void push(long long st,long long ans)//key->value
    {
        int i;
        int h=st%HASH;
        for(i=head[h];i!=-1;i=next[i])//这里要注意是next
          if(state[i]==st)//找到了此键值
          {
              f[i]+=ans;//键值已存在,在这种状态下只是把次数加进去就好啦
              return;
          }
        state[size]=st;
        f[size]=ans;
        next[size]=head[h];
        head[h]=size++;
    }
}hm[2];
void decode(int *code,int m,long long  st)//把某行上的轮廓信息解成一个code数组
{
    for(int i=m;i>=0;i--)
    {
        code[i]=st&7;//要是只有2中状态就&1呗
        st>>=3;
    }
    isend=st&1;///!!!
}
long long encode(int *code,int m)//最小表示法 m<=12显然只有6个不同的连通分量
{
    int cnt=1;
    memset(ch,-1,sizeof(ch));
    ch[0]=0;
    long long st=isend;///!!!
    for(int i=0;i<=m;i++)
    {
        if(ch[code[i]]==-1)ch[code[i]]=cnt++;//新发现一个
        code[i]=ch[code[i]];
        st<<=3;//0~7 8进制表示
        st|=code[i];//<==>st+=code[i]
    }
    return st;//返回最终次轮廓上的连通分量信息
}
void shift(int *code,int m)
{
    for(int i=m;i>0;i--)code[i]=code[i-1];
    code[0]=0;
}

void dpblank(int i,int j,int cur)
{
    int k,left,up;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);
        left=code[j-1];
        up=code[j];
        if(isend)
        {//如果已经形成环路,后面又有插头或者有必过点,则是非法的
            if(left||up||maze[i][j]==2)continue;
            code[j-1]=code[j]=0;
            if(j==M)shift(code,M);
            hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            continue;
        }
        if(left&&up)
        {
            if(left==up)
            {
                code[j-1]=code[j]=0;
                isend=1;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
            else //不在一个连通分量则合并
            {
                code[j-1]=code[j]=0;
                for(int t=0;t<=M;t++)
                  if(code[t]==up)
                    code[t]=left;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
        else if((left&&(!up))||((!left)&&up))
        {
            int t;
            if(left)t=left;
            else t=up;
            if(maze[i][j+1])
            {
                code[j-1]=0;
                code[j]=t;
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
            if(maze[i+1][j])
            {
                code[j-1]=t;
                code[j]=0;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
        else
        {
            if(maze[i][j+1]&&maze[i+1][j])
            {
                code[j-1]=code[j]=13;
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
            if(maze[i][j]==1)//可以经过可以不经过的点
            {
                code[j-1]=code[j]=0;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
    }
}
void dpblock(int i,int j,int cur)
{
    int k;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);
        code[j-1]=code[j]=0;
        if(j==M)shift(code,M);
        hm[cur^1].push(encode(code,M),hm[cur].f[k]);
    }
}
char str[20];
void init()
{
    scanf("%d%d",&N,&M);
    memset(maze,0,sizeof(maze));
    for(int i=1;i<=N;i++)
    {
        scanf("%s",&str);
        for(int j=1;j<=M;j++)
        {
            if(str[j-1]=='*')maze[i][j]=1;
            else if(str[j-1]=='O')maze[i][j]=2;
        }
    }
}
void solve()
{
    int i,j,cur=0;
    hm[cur].init();
    hm[cur].push(0,1);
    for(int i=1;i<=N;i++)
      for(int j=1;j<=M;j++)
      {
          hm[cur^1].init();
          if(maze[i][j])dpblank(i,j,cur);
          else dpblock(i,j,cur);
          cur^=1;
      }
    long long ans=0;
    for(i=0;i<hm[cur].size;i++)
      ans+=hm[cur].f[i];
    printf("%I64d\n",ans);
}
int main()
{
  //  freopen("cin.txt","r",stdin);
    int T;
    int iCase=0;
    scanf("%d",&T);
    while(T--)
    {
        iCase++;
        printf("Case %d: ",iCase);
        init();
        solve();
    }
    return 0;
}