2019.9.1 涂抹果酱

 

 


 

还是简单的状压dp

用dp[i][j]表示前i行最后一行状态是j的方案总数

则每次判断当前是普通行、已经固定的行、固定行的下一行

如果是普通行 dp[i][j]+=dp[i-1][l]

如果是固定行 记已经固定好的状态编号是flag 则dp[i][flag]+=dp[i-1][l]且dp[i][j](j!=flag)=0

如果是固定行的下一行 则dp[i][j]+=dp[i-1][flag]

注意由于是1-3所以用4进制状态压缩 所有含有0的状态全部排除

上代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
int cnt,num[10050],dp[10050][(2<<12)+1];
int n,m,k,mapp[10050],ans,book;
bool check(int x)
{
    int flag=-1;
    if(x==0)return false;
    int idx=0;
    int temp=x;
    while(temp)
    {
        if(!(temp%4))return false;
        idx++;
        temp/=4;
    }
    if(idx<m)return false;
    while(x)
    {
        if(flag==x%4)return false;
        flag=x%4;
        x/=4;
    }
    return true;
}//判断状态本身是否合法
bool cannot(int x,int y)
{
    while(x)
    {
        int p=x%4,q=y%4;
        if(p==q)return true;
        x/=4,y/=4;
    }
    return false;
}//判断两个状态能否作为上下两行同时出现
signed main()
{
    scanf("%lld%lld",&n,&m);
    scanf("%lld",&k);
    for(int i=1;i<=m;i++)scanf("%lld",&mapp[i]),book=book*4+mapp[i];
    if(!check(book))//特判,如果固定行的状态不合法
    {
        putchar('0');
        return 0;
    }
    int maxn=1;
    for(int i=1;i<=m;i++)maxn*=4;
    --maxn;
    for(int i=0;i<=maxn;i++)
        if(check(i))//枚举可能的状态
        {
            //printf("%d\n",i);
            num[++cnt]=i,dp[1][cnt]=1;
        }
    int flag=-1;
    for(int j=1;j<=cnt;j++)
        if(num[j]==book)
        {
            flag=j;
            break;
        }//记录固定行状态编号
    for(int i=1;i<=cnt;i++)
        if(i!=flag)dp[k][i]=0;//将所有固定行没有选择固定行状态的dp值全部置0
    for(int i=2;i<=n;i++)
    {
        if(i==k)//固定行
        {
            for(int j=1;j<=cnt;j++)
                if(j!=flag)dp[i][j]=0;
            for(int l=1;l<=cnt;l++)
                if(!cannot(num[l],book))dp[i][flag]=(dp[i][flag]+dp[i-1][l])%1000000;//枚举上一行的状态,记得加的时候取模
            continue;
        }
        for(int j=1;j<=cnt;j++)
        {
            int x=num[j];
            if(i==k+1)//固定行的上一行
            {
                if(cannot(x,book))continue;
                dp[i][j]=(dp[i][j]+dp[i-1][flag])%1000000;
                continue;
            }
            for(int l=1;l<=cnt;l++)
            {
                int y=num[l];
                if(cannot(x,y))continue;
                dp[i][j]=(dp[i][j]+dp[i-1][l])%1000000;
            }//普通行
        }
    }
    for(int i=1;i<=cnt;i++)
    {
        ans=(ans+dp[n][i])%1000000;//枚举最后一行可能的状态
        //printf("%lld ",ans);
    }
    printf("%lld",ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/qxds/p/11442096.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值