• HDU 4064 Carcassonne


    Carcassonne
    Problem Description
    Carcassonne is a tile-based board game for two to five players.
    Square tiles are printed by city segments,road segments and field segments. 

    The rule of the game is to put the tiles alternately. Two tiles share one edge should exactly connect to each other, that is, city segments should be linked to city segments, road to road, and field to field. 

    To simplify the problem, we only consider putting tiles:
    Given n*m tiles. You can rotate each tile, but not flip top to bottom, and not change their order. 
    How many ways could you rotate them to make them follow the rules mentioned above?
     

     Input

    The first line is a number T(1<=T<=50), represents the number of case. The next T blocks follow each indicates a case.
    Each case starts with two number N,M(0<N,M<=12)
    Then N*M lines follow,each line contains M four-character clockwise.
    'C' indicate City.
    'R' indicate Road.
    'F' indicate Field.
     

     Output

    For each case, output the number of ways mod 1,000,000,007.(as shown in the sample output)
     

     Sample Input

    3
    1 1
    RRRR
    1 2
    RRRF FCCC
    8 8
    FCFF RRFC FRCR FRFR RCCR FFCC RRFF CRFR
    FRRC FRFR CCCR FCFC CRRC CRRR FRCR FRFR
    RRCR FRRR CCCR FFFC RRFF RFCR CCFF FCCC
    CFCF RRFF CRFR FFRR FRRF CCRR FFFC CRRF
    CFRR FFFF FFFF RRFF RRRR RCRR FFCC RFRF
    RRCF FRFR FRRR FRFR RCCR RCCC CFFC RFRF
    CFCF FRFF RRFF FFFF CFFF CFFF FRFF RFRR
    CCRR FCFC FCCC FCCC FFCC FCCF FFCC RFRF
     

    Sample Output

    Case 1: 4
    Case 2: 1
    Case 3: 1048576
     

    Source

     
     
    题意:给出一块n×m的拼图板,每块拼图的四条边都代表着一些景物,C表示城市,R表示路,F表示田野。很显然,对于互不相同的两块拼图只有边缘代表的景物相同才能拼在一起。规定拼图本身位置不能变,只能转向,问有多少种拼法。对于每一组输入,是n行m列长度为4的字符串。每一个字符串的含义是按顺时针顺序给出拼图边缘代表的景物。
     
    题解:对于n,m那么小的数据,想必能很快想到搜索。但是很显然,搜索的状态数太多了,不便于快速解答。
    于是我们便想到了用状态压缩DP来解决此题。
    一般很多状压都是二进制的,但针对本题三种景物,我们自然选择三进制的状压。
    对于每一行,状态总个数的最大值为312=531441,这样不会特别大,在可接受范围内。
    接下来讲如何状压。
    首先我们应该想到对于每一行,我们可以根据上一行底端出现的状态数去推导下一行的状态数,因为上一行的底端就是下一行的顶端,这样我们就能连贯地求出下一行的底端,并把状态数不断下推,直到最后一行。
    紧接着就是处理单独的一行。对于单独的一行就包含很多的状态,我们用DFS处理更加方便。首先此行第一列肯定朝四个方向摆都行,我们就直接摆,之后的摆法必须建立于后一列的左端与前一列的右端相等,这样摆完一行后,我们把这一行顶端的情况对应到上一行底端的情况,并把上一行的状态数加入这一行。
    梳理好这两步,该题的主要框架就建立好了。
    接下来就是一些细节,下面是我的体验:
    1、预处理3的幂次方时,我naive地处理到11以为就够了,于是WA。
    2、最后输出时想当然在case后加了#号,于是WA。
    3、对于一块拼图,如果4条边都相等,说明无论朝哪个方向摆都能产生相同的贡献,且这样相同的贡献重复了4次,所以我们记录一个mul,遇到4条边相同的拼图可以直接将mul乘以4。到边界处理时记牢把上一行的状态数乘以mul再加进这一行的状态统计。这样就可以获得强大的剪枝效果。如果没有这个剪枝就会TLE。当然后面的DFS结束后,要记牢把mul除以4还原。
    4、注意f[i][j]表示的含义是在第i行出现了状态j,且j代表第i行底端的状态。声明这里是为了防止有人把DFS中遇到边界处理记录状态理解错。
    5、如果空间开太大会MLE,因此要用滚动数组记录状态。
    6、没有了。
    7、真的没有了。
    好了,下面附上我的代码。
     1 #include<bits/stdc++.h>
     2 #define mod 1000000007
     3 using namespace std;
     4 const int N=15,S=531441;
     5 int ca,t,n,m,cur,mul,base[13],g[N][N][4];
     6 long long ans,f[2][S];
     7 char s[5];
     8 int change(char x){
     9     return x=='C'?0:(x=='R'?1:2);
    10 }
    11 void dfs(int c,int num,int up,int down,int right)
    12 {
    13     if (num>m)
    14     {
    15         if (c==1) f[cur][down]=(f[cur][down]+mul)%mod;
    16         else f[cur][down]=(f[cur][down]+f[cur^1][up]*mul)%mod;
    17         return;
    18     }
    19     if (g[c][num][0]==g[c][num][1]&&g[c][num][1]==g[c][num][2]&&g[c][num][2]==g[c][num][3])
    20     {
    21         int add=g[c][num][0]; mul*=4;
    22         if (right==-1||right==add) dfs(c,num+1,up*3+add,down*3+add,add);
    23         mul/=4; return;
    24     }
    25     for (int i=0;i<4;++i)
    26     {
    27         int U=g[c][num][i];
    28         int R=g[c][num][(i+1)%4];
    29         int D=g[c][num][(i+2)%4];
    30         int L=g[c][num][(i+3)%4];
    31         if (right==-1||right==L) dfs(c,num+1,up*3+U,down*3+D,R);
    32     }
    33 }
    34 int main()
    35 {
    36     scanf("%d",&t); base[0]=1;
    37     for (int i=1;i<=12;++i)
    38     base[i]=base[i-1]*3;
    39     while (t--)
    40     {
    41         scanf("%d%d",&n,&m);
    42         for (int i=1;i<=n;++i)
    43         for (int j=1;j<=m;++j)
    44         {
    45             scanf("%s",s);
    46             for (int k=0;k<4;++k)
    47             g[i][j][k]=change(s[k]);
    48         }
    49         memset(f,0,sizeof(f));
    50         cur=ans=0;
    51         for (int i=1;i<=n;++i)
    52         {
    53             cur^=1; mul=1;
    54             dfs(i,1,0,0,-1);
    55             for (int j=0;j<base[m];++j)
    56             f[cur^1][j]=0;
    57         }
    58         for (int i=0;i<base[m];++i)
    59         ans=(ans+f[cur][i])%mod;
    60         printf("Case %d: %d
    ",++ca,ans);
    61     }
    62     return 0;
    63 }
    View Code
  • 相关阅读:
    【制作】基于金沙滩51单片机的电子密码锁程序
    【制作】基于51单片机的蓝牙遥控小车方案
    【制作】基于金沙滩51单片机的贪吃蛇程序
    【单片机】滑稽AT89C52表情实现
    【错误解决】Android APK 方法数量限制
    【教程】C语言入门
    fastdfs分布式安装教程
    xposed绕过ssl校验新玩具
    最新get两款脱壳工具
    app逆向万能的md5加密hook破解入参方法(其他加密用通用方法原理差不多,小白推荐)
  • 原文地址:https://www.cnblogs.com/zk1431043937/p/7750806.html
Copyright © 2020-2023  润新知