Problem:
JOI 旗 (JOI Flag)

問題

情報オリンピック日本委員会では，今年の日本情報オリンピック (JOI) を宣伝するために， JOI のロゴをモチーフにした旗を作ることになった．旗は「良い旗」でなければならない．「良い旗」とは，アルファベットの J, O, I のいずれかの文字を，縦 M 行，横 N 列の長方形状に並べたもので， J, O, I が以下の図のように (すなわち，J の右隣に O が，その J の下隣に I が) 並んでいる箇所が旗の少なくとも 1 か所にあるものである．

以下の図に，「良い旗」の例を 2 つ示す．

以下の図に，「良い旗」ではない例を 2 つ示す．

いま M, N の値，および旗の一部の場所について J, O, I のどの文字にするかが決まっており，入力としてその情報が与えられる．考えられる「良い旗」は何通りあるかを計算し，その数を 100000 (=105) で割った余りを出力するプログラムを作成せよ．

入力

入力は 1 + M 行からなる．

1 行目には旗の大きさを表す 2 つの整数 M, N (2 ≤ M ≤ 20, 2 ≤ N ≤ 20) が空白で区切られて書かれている．

1 + i 行目 (1 ≤ i ≤ M) には，N 文字からなる文字列が書かれている．各文字は J, O, I, ? のいずれかであり， j 文字目 (1 ≤ j ≤ N) が J, O, I のいずれかである場合は i 行 j 列の場所の文字がそれぞれ J, O, I に決まっていること， ? である場合はまだ決まっていないことを表す．

出力

考えられる「良い旗」の個数を 100000 (=105) で割った余りを 1 行で出力せよ．

入出力例

入力例 1

2 3
??O
IIJ

出力例 1

4

入力例 1 においては，以下の図の 4 通りの「良い旗」が考えられる．

入力例 2

2 2
??
??

出力例 2

3

入力例 3

3 3
??I
???
O?J

出力例 3

53

入力例 4

5 4
JOI?
????
????
????
?JOI

出力例 4

28218

入力例 4 においては，「良い旗」は 2428218 通り考えられるので，それを 100000 (=105) で割った余りである 28218 を出力する．

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C (judge verdict: Time Limit Exceeded)

/*
AizuOnline A0559
JOI Flag
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define DEBUG 0

char pattern[20][21];
int M,N;
int empty;

int is_good_flag();
long power3();
int search();
void init_pattern();
int count_empty();

main()
{
  long ret;

  init_pattern();
  empty=count_empty();
  ret = search(empty);
  printf("%ld\n",ret % 100000);

  return(0);
}

void init_pattern()
{
  int i;
  char buf[100];

  scanf("%d %d",&M,&N);
  for(i=0;i<M;i++)
    {
      scanf("%s",buf);
      strncpy(&(pattern[i][0]),buf,N);
    }

}

int count_empty()
{  
  int i,j,cnt;

  cnt = 0;
  for(i=0;i<M;i++)
    for(j=0;j<N;j++)
      if(pattern[i][j] == '?')
	cnt++;
  return(cnt);
}

void next_empty_position(int * x,int * y)
{
  int i,j;

  for(i=0;i<M;i++)
    for(j=0;j<N;j++)
      if(pattern[i][j]=='?' && is_good_flag_before(i,j))
	{ 
	  *x=i;*y=j;
	  return;
	}
  for(i=0;i<M;i++)
    for(j=0;j<N;j++)
      if(pattern[i][j]=='?')
	{
	  *x=i;*y=j;
	  return;
	}
  return;
}
int is_good_flag(int x,int y)
{
  if(pattern[x][y] == 'J')
    {  if(y < N-1 && pattern[x][y+1]=='O' && x < M-1 && pattern[x+1][y]=='I')
	return(-1);
      else
	return(0);
    }
  if(pattern[x][y] == 'O')
    {  if(y > 0 && pattern[x][y-1]=='J' && x < M-1 && pattern[x+1][y-1]=='I')
	return(-1);
      else
	return(0);
    }
  if(pattern[x][y] == 'I')
    {  if(x > 0 && pattern[x-1][y]=='J' && y < N-1 && pattern[x-1][y+1]=='O')
	return(-1);
      else
	return(0);
    }
}
int is_good_flag_before(int x,int y)
{   
  /*
  printf("x,y=%d,%d\n",x,y);
  */
    if((y < N-1 && pattern[x][y+1]=='O' && x < M-1 && pattern[x+1][y]=='I')||
       (y > 0 && pattern[x][y-1]=='J' && x < M-1 && pattern[x+1][y-1]=='I')||
       (x > 0 && pattern[x-1][y]=='J' && y < N-1 && pattern[x-1][y+1]=='O'))
	return(-1);
      else
	return(0);
    
}
void print_pattern()
{
  int i,j;
  for(i=0;i<M;i++)
    {
      for(j=0;j<N;j++)
	printf("%c",pattern[i][j]);
      printf("\n");
    }
  printf("---\n");
}
int search(int empty)
{
  int x,y;
  int i;
  long cnt;
  extern long power3(int);

  if(empty == 0)
    return(0);

  next_empty_position(&x,&y);
#if DEBUG
  printf("N.E.=%d,%d\n",x,y);
  exit(0);
#endif
  cnt = 0;
  for(i=0;i<3;i++)
    {
      pattern[x][y]="JOI"[i];
#if 0	  
      print_pattern();
#endif
      if(is_good_flag(x,y))
	{
#if 0
	  print_pattern();
#endif
	  cnt += power3(empty-1);
	}
      else
	cnt += search(empty-1);
    }
  pattern[x][y]='?';
  return(cnt);
}

long power3(int x)
{
  static long p3[]={1,3,9,27,81,243,729,2187,6561,\
		    19683, 59049, 77147, 31441, 94323};

  if(x<(sizeof(p3)/sizeof(long)))
    return(p3[x]);
  else
    return (3*power3(x-1) % 100000);

}