zoukankan      html  css  js  c++  java
  • UESTC 885 方老师买表 --状压DP

    将方格的摆放分成两种:

    1.水平摆放:此时所占的两个格子都记为1。

    2.竖直摆放:此时底下那个格子记为1,上面那个记为0。

    这样的话,每行都会有一个状态表示。

    定义:dp[i][s]表示考虑已经填到第i行,这一行状态为s的方法数

    转移:dp[i][s] = dp[i][s]+dp[i-1][s']  (s'为上一行的状态,当第i行和第i-1行能够满足条件时,进行转移)

    先预处理出所有满足条件的第一行,然后从第二行开始转移。

    最后答案为dp[n][(1<<m)-1].

    当n<m时交换n和m可减小1<<m,即减少状态数。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define Mod 1000000007
    #define ll long long
    using namespace std;
    #define N 2100
    
    ll dp[13][N];
    int n,m;
    
    int FirstLine(int state)
    {
        int i=0;
        while(i<m)
        {
            if(state & (1<<i))   //第i列为1,第i+1列也存在且必须为1
            {
                if(i < m-1)
                {
                    if(state & (1<<(i+1)))   //第i+1列为1
                        i += 2;
                    else
                        return 0;
                }
                else
                    return 0;
            }
            else
                i++;
        }
        return 1;
    }
    
    int Can(int ka,int kb)  //ka:这一行,kb:上一行
    {
        int i = 0;
        while(i<m)
        {
            if(ka & (1<<i))  //这一行i列为1
            {
                if(kb & (1<<i))  //如果上一行i列为1,则为两个水平块
                {
                    if(i < m-1 && (ka & (1<<(i+1))) && (kb & (1<<(i+1))))
                        i += 2;
                    else
                        return 0;
                }
                else    //上一行为0,竖着放的
                    i++;
            }
            else   //这一行i列为0,上一行i列必须填充
            {
                if(kb & (1<<i))
                    i++;
                else
                    return 0;
            }
        }
        return 1;
    }
    
    int main()
    {
        int i,j,sa;
        int state1,state2;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            if((n*m)%2)
            {
                puts("0");
                continue;
            }
            memset(dp,0,sizeof(dp));
            if(n < m)
                swap(n,m);
            int MAX = (1<<m)-1;
            for(sa=0;sa<=MAX;sa++)
            {
                if(FirstLine(sa))   //此状态可以作为第一行的状态
                    dp[0][sa] = 1;
            }
            for(i=1;i<n;i++) //行递增
            {
                for(state1=0;state1<=MAX;state1++)
                {
                    for(state2=0;state2<=MAX;state2++)
                    {
                        if(Can(state1,state2))
                            dp[i][state1] += dp[i-1][state2];
                    }
                }
            }
            printf("%lld
    ",dp[n-1][MAX]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    主键、外键
    SpringBoot定时任务Scheduled
    启动报DataSource错误
    SpringBoot整合aop
    元数据MetaData(五)
    普通结果集ResultSet和离线结果集RowSet(四)
    Statements、PreparedStatement及CallableStatement(三)
    JDBC数据库连接(二)
    JDBC简介(一)
    【Oracle】常用函数
  • 原文地址:https://www.cnblogs.com/whatbeg/p/3762745.html
Copyright © 2011-2022 走看看