hdu 4363 Draw and paint(dp计数,5级)

D - Draw and paint
Time Limit:3000MS     Memory Limit:98304KB     64bit IO Format:%I64d & %I64u

Description

Shadow is interested in the four-color theorem, now he wants to draw some lines on a rectangle paper to divide it into some parts, and then paint these parts with four different colors, the color of two adjacent parts must be different.  Firstly, Shadow draws a horizontal line and divides the paper into two parts (The height and width of two parts must be integer), then he paints one part with a kind of color.  Then he draws a vertical line and divides the other part into two parts (The height and width of two parts must be integer), then he paints one part with a kind of color.  Then he draws a horizontal line……That is to say, Shadow draws a horizontal line during an odd step, and a vertical line during an even step. Shadow repeats these operations until he can’t or doesn’t want to anymore. Shadow can’t rotate or overturn the paper, do you know how many different ways can he draw lines and paint colors.
 

Input

The first line is one integer T indicates the number of test cases. (T <= 1000) Then for each test case, there are two integers n and m ( 0 < n,m <= 40 )in one line, indicates the height and width of the rectangle paper.
 

Output

For each test case,output one integer in a line, the amount of ways can Shadow draw and paint mod 1000000007.
 

Sample Input

3 1 1 1 2 2 1
 

Sample Output

4 4 16
 

题目大意:

给你一个n*m ( 0 < n,m <= 40 )的矩形,给你四种颜色,一个人切这个矩形,第一次水平切,切玩后选择一块矩形给他染上一种颜色,第二次垂直切未染色的那块矩形,切玩后选择新增的两块矩形中的一块给他染上一种颜色,第三次水平切……如此循环反复,保证每次染色时相邻矩形染色不一样,问你最后的方案数mod 1000000007。T (T <= 1000)组测试数据。

题解:

用f[i][j][a][b][c][d][2]表示长为i宽为j,四边颜色分别为a、b、c、d,并且当前需要画横线还是竖线时的总方案数,然后枚举画线位置以及其中一部分所染的颜色进行状态转移。颜色表示中需要多加一种表示未染色,即5种。

实现用记忆化搜索。

此题若超时的原因在于f[i][j][a][b][c][d][2]是一定的,搜出来之后不必每次在新的测试点都重新memset

此题的trick在于,单独的f[i][j][a][b][c][d][2]方程是不对的,还要减去重复的部分,读者可以拿来3*1分析。

且程序中对此做了标记。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
const LL mod=1000000007;
LL dp[42][42][5][5][5][5][3];
void DP(int h,int w,int a,int b,int c,int d,int ki)
{
  if(dp[h][w][a][b][c][d][ki]!=-1)return;
  LL &ret=dp[h][w][a][b][c][d][ki];
  ret=0;
  for(int i=1;i<=4;++i)
    if(i!=a&&i!=b&&i!=c&&i!=d)
    ++ret;
  if(ki==1)///水平切
  {
    for(int i=1;i<=h-1;++i)
      {
        for(int cor=1;cor<=4;++cor)//选择上方涂色
        {
          if(cor!=a&&cor!=b&&cor!=c)
          {
            DP(h-i,w,a,cor,c,d,0);
            ret=(ret+dp[h-i][w][a][cor][c][d][0])%mod;
          }
        }
        for(int cor=1;cor<=4;++cor)///下方涂色
        {
          if(cor!=a&&cor!=c&&cor!=d)
          {
            DP(i,w,a,b,c,cor,0);
            ret=(ret+dp[i][w][a][b][c][cor][0])%mod;
          }
        }
      }
    LL kill=0;
    for(int co=1;co<=4;++co)
      if(co!=a&&co!=c&&co!=d)
        for(int cor=1;cor<=4;++cor)
        if(cor!=co&&cor!=a&&cor!=b&&cor!=c)
        ++kill;
        kill=kill*(h-1);
      ret=(ret+mod-kill)%mod;
  }
  else ///竖直切
  {
    for(int i=1;i<=w-1;++i)
    {
      for(int cor=1;cor<=4;++cor)///左涂色
      {
        if(cor!=a&&cor!=b&&cor!=d)
        {
          DP(h,w-i,cor,b,c,d,1);
          ret=(ret+dp[h][w-i][cor][b][c][d][1])%mod;
        }
      }
      for(int cor=1;cor<=4;++cor)
      {
        if(cor!=b&&cor!=c&&cor!=d)
        {
          DP(h,i,a,b,cor,d,1);
          ret=(ret+dp[h][i][a][b][cor][d][1])%mod;
        }
      }
    }
    LL kill=0;
    for(int co=1;co<=4;++co)
      if(co!=a&&co!=b&&co!=d)
      for(int cor=1;cor<=4;++cor)
      if(cor!=co&&cor!=b&&cor!=c&&cor!=d)
      ++kill;
      kill=kill*(w-1);
      ret=(ret+mod-kill)%mod;
  }
}
int main()
{
  int cas,n,m;
  memset(dp,-1,sizeof(dp));
  while(~scanf("%d",&cas))
  {
    while(cas--)
    {
      scanf("%d%d",&n,&m);
      DP(n,m,0,0,0,0,1);
      printf("%I64d\n",dp[n][m][0][0][0][0][1]);
    }
  }
  return 0;
}

 

posted @ 2013-07-22 15:15  剑不飞  阅读(303)  评论(0编辑  收藏  举报