CodeForces 77 D.Domino Carpet(dp)

202 篇文章 1 订阅

Description

给出七张牌的水平竖直状态如下图:
这里写图片描述
这里写图片描述
两张水平牌可以组成一个 1×2 1 × 2 块,两张竖直牌可以组成一个 2×1 2 × 1 块,给出由这些牌组成的 n×m n × m 矩阵,要求把该矩阵划分成若干 1×2 1 × 2 块和 2×1 2 × 1 块的不交并,且需满足所有 1×2 1 × 2 块的左边块所处列不相邻,问划分方案数

Input

第一行两个整数 n,m(1n,m250) n , m ( 1 ≤ n , m ≤ 250 ) 表示矩阵行列数,之后输入该 n×m n × m 矩阵

Output

输出方案数,结果模 109+7 10 9 + 7

Sample Input
这里写图片描述
Sample Output

3

Solution

所有 1×2 1 × 2 块的左边块所处列不相邻的限制使得有 1×2 1 × 2 的两列与其他列完全分离,以 dp[j] d p [ j ] 表示前 j j 列划分的方案数,那么若第j列可以由若干 2×1 2 × 1 块组成则有转移 dp[j]+=dp[j1] d p [ j ] + = d p [ j − 1 ] ,然后考虑第 j j 列和第j1列这两列出现 1×2 1 × 2 块的方案数,以 f[i][0/1] f [ i ] [ 0 / 1 ] 分别表示这两列前 i i 行完全划分且不出现/出现2×1块的方案数,则有以下转移

f[0][0]=1,f[0][1]=0 f [ 0 ] [ 0 ] = 1 , f [ 0 ] [ 1 ] = 0

若第 i i 行的两个块可以组成1×2块则 f[i][1]=f[i1][0]+f[i1][1] f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ]

若第 i1 i − 1 行和第 i i 行这四个块可以组成两个2×1块则 f[i][0]=f[i2][0],f[i][1]+=f[i2][1] f [ i ] [ 0 ] = f [ i − 2 ] [ 0 ] , f [ i ] [ 1 ] + = f [ i − 2 ] [ 1 ]

最后 f[n][1] f [ n ] [ 1 ] 即为这两列出现 1×2 1 × 2 块的方案数,继而有转移 dp[j]+=dp[j2]f[n][1] d p [ j ] + = d p [ j − 2 ] ⋅ f [ n ] [ 1 ] dp[m] d p [ m ] 即为答案

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=255;
#define mod 1000000007
int n,m,type[maxn][maxn],dp[maxn],f[maxn][2];
char s[3][maxn*5];
//0 hor
//1 ver
//2 both 
int Solve(int L,int R)
{
    int num=0;
    for(int i=0;i<3;i++)
        for(int j=L;j<=R;j++)
            if(s[i][j]=='O')num++;
    if(num==2||num==3)
    {
        if(s[0][R]=='O')return 1;
        return 0;
    }
    if(num==6)
    {
        if(s[1][L]=='O')return 1;
        return 0;
    }
    return 2;
}
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",s[0]);
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<3;j++)scanf("%s",s[j]);
        int L=1,R=3;
        for(int j=1;j<=m;j++)
        {
            type[i][j]=Solve(L,R);
            L+=4,R+=4;
        }
        scanf("%s",s[0]);
    }
    //for(int i=1;i<=n;i++)
    //  for(int j=1;j<=m;j++)
    //      printf("%d%c",type[i][j],j==m?'\n':' ');
    dp[0]=1;
    for(int j=1;j<=m;j++)
    {
        if(n%2==0)
        {
            int flag=1;
            for(int i=1;i<=n;i++)
                if(type[i][j]==0)
                {
                    flag=0;
                    break;
                }
            if(flag)dp[j]=(dp[j]+dp[j-1])%mod;
        }
        if(j>1)
        {
            f[0][0]=1,f[0][1]=0;
            for(int i=1;i<=n;i++)
            {
                f[i][0]=f[i][1]=0;
                if(type[i][j-1]!=1&&type[i][j]!=1)f[i][1]=(f[i-1][0]+f[i-1][1])%mod;
                if(i==1)continue;
                if(type[i-1][j-1]&&type[i-1][j]&&type[i][j-1]&&type[i][j])
                {
                    f[i][1]=(f[i][1]+f[i-2][1])%mod;
                    f[i][0]=(f[i][0]+f[i-2][0])%mod;
                }
            }
            dp[j]=(dp[j]+(ll)dp[j-2]*f[n][1]%mod)%mod;
        }
    }
    printf("%d\n",dp[m]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值