例题:

最近小A遇到了一个很有趣的问题:
现在有一个\(n\times m\)规格的桌面,我们希望用\(1 \times 2\)规格的多米诺骨牌将其覆盖。
例如,对于一个\(10 \times 11\)的桌面,下面为一种合法覆盖方案:

那么给定n、m,应该如何覆盖呢?
但是小A并不满足于覆盖桌面,他希望知道能够覆盖整个桌面的合法方案数。
输入有 \(t\) 组数据,对于每组数据:
输入\(n、m\),输出合法方案数\(ans\),答案\(ans\)对\(10^9+7\)取模,每个答案占一行。
以输入两个\(0\)为终止标志,输入保证\(n \times m\)为偶数 , \(t \leq 5\)。
***

子任务:

数据范围1 : \(n、m\leq 4\)

咳咳咳.... 滑稽吧。
直接暴力DFS一下即可。
复杂度:\(O(?)\)
***

数据范围2 : \(n、m \leq 10\)

思路解析:

以每一行来状压DP,假设\(m \leq n\):
用一个二进制数表示每一行的状态,一共有: \(2^m\) 种,1代表放了,0代表没放。
那么每次枚举上一次的状态\(k\),枚举这一次的状态\(j\),然后检查一下是否可以转移。
如果可以,直接\(f[i][j] = f[i][j] + f[i-1][k];\)
可以转移的条件 自己yy ,考虑三种情况:不放,横放,竖放。
一个小技巧:竖放的情况只在后面那一格填1,保证合法。
时间复杂度\(O(t \times n \times {(2^m)}^2)\)

实现代码:

#include<bits/stdc++.h>
using namespace std;

long long f[13][2048]; int n,m,mx;

bool check(int s,int t){
    int r = 0,g = 0;
    for(int i = 0; i < m; i ++)
        r |= ((s&(1<<i)) ? 0:(1<<i));
    if((r&t) != r)return false;
    r = 0;
    for(int i = 0; i <= m; i ++){
        if(((1<<i)&s) && ((1<<i)&t))g = (g+1)%2;
        else if(g)return false;
    }
    return true;
}

int main(){
    while(scanf("%d %d\n",&n,&m)){
        if(!n && !m)break;
        if(n < m)swap(n , m);
        memset(f,0,sizeof(f));
        mx = (1<<m) - 1;
        f[0][mx] = 1;
        for(int i = 1; i <= n; i ++)
            for(int j = 0; j <= mx; j ++)
                for(int k = 0; k <= mx; k ++)
                    if(check(k,j))f[i][j] += f[i-1][k];
        cout<<f[n][mx]<<endl;
    }return 0;
}

数据范围3: \(n、m \leq 15\)

思路解析:

显然上一种状压\(DP\)会超时
以每一格来状压DP,假设\(m \leq n\),
把棋盘从上往下,从左往右分为\(n \times m\)个阶段,每一个格子对应一个。
那么把轮廓线压入状态中,一共有\(2^m\)种状态。
如图:

图中黄色格子为当前决策格,他有两种决策,放或者不放。
那么当前枚举的历史状态\(k\)为{ \(k_4k_3k_2k_1k_0\) }
那么对应的三种放置方法:
不放:新状态为{ \(k_3k_2k_1k_00\) } , 条件为\(k4=1\)
上放:新状态为{ \(k_3k_2k_1k_01\) } , 条件为\(k4=0\)并且当前不是第一行。
右放:新状态为{ \(k_3k_2k_111\) } , 条件为\(k4-1\)并且当前不是第一列。
还不懂得怎么设状态的可以去看刘汝佳(ORZ)的训练指南(蓝书)的P384-386。
时间复杂度:\(O(t \times n \times m \times 2^m)\)

实现代码:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

long long f[2][2048]; int cur,n,m,mx;

int main(){
    freopen("testdate.in","r",stdin);
    while(scanf("%d %d\n",&n,&m)){
        if(!n && !m)break;
        if(n < m)swap(n , m);
        mx = (1<<m) - 1; cur = 0;
        memset(f,0,sizeof(f));
        f[cur][mx] = 1;
        for(int i = 1; i <= n; i ++)
            for(int j = 0; j < m; j ++){
                cur^=1;
                memset(f[cur],0,sizeof(f[cur]));
                //自上往下三种转移分别为:不放、上方、右放。
                for(int k = 0; k <= mx; k ++){
                    if(k & (1<<(m-1))){
                        f[cur][(k<<1)^(1<<m)] += f[cur^1][k];
                        if(j && !(k&1))f[cur][((k<<1)|3)^(1<<m)] += f[cur^1][k];
                    }
                    else if(i)f[cur][(k<<1)|1] += f[cur^1][k];
                }

            }
        printf("%lld\n",f[cur][mx]);
    }return 0;
}

多米诺骨牌放置问题(状压DP)的更多相关文章

  1. poj 2411 Mondriaan's Dream 骨牌铺放 状压dp

    题目链接 题意 用\(1\times 2\)的骨牌铺满\(H\times W(H,W\leq 11)\)的网格,问方案数. 思路 参考focus_best. 竖着的骨牌用\(\begin{pmatri ...

  2. 省选训练赛第4场D题(多米诺骨牌)

    题目来自FZU2163 多米诺骨牌 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem Description Vasya很喜欢排多米诺 ...

  3. P1282 多米诺骨牌

    P1282 多米诺骨牌 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S ...

  4. P1282 多米诺骨牌[可行性01背包]

    题目来源:洛谷 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+ ...

  5. 【Tsinghua OJ】多米诺骨牌(domino)问题

    (domino.c/cpp)[问题描述] 小牛牛对多米诺骨牌有很大兴趣,然而她的骨牌比较特别,只有黑色和白色的两种.她觉 得如果存在连续三个骨牌是同一种颜色,那么这个骨牌排列便是不美观的.现在她有n个 ...

  6. 【01背包】洛谷P1282多米诺骨牌

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  7. P1282 多米诺骨牌 (背包变形问题)

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  8. [LeetCode] Push Dominoes 推多米诺骨牌

    There are N dominoes in a line, and we place each domino vertically upright. In the beginning, we si ...

  9. [Luogu1282]多米诺骨牌(DP)

    #\(\color{red}{\mathcal{Description}}\) \(Link\) 我们有一堆多米诺骨牌,上下两个部分都有点数,\(But\)我们有一个操作是可以对调上下的点数.若记一块 ...

随机推荐

  1. javamail+ical4j发送会议提醒

    本篇讲述小编在使用ical4j时对其的理解与使用,留作笔记的同时希望能帮助到大家! 初学者可以先了解下ical4j的基本信息: iCalender编程基础,了解与使用ical4j:https://ww ...

  2. zabbix邮件报警设置

    第一.安装邮件发送工具mailx 这里我选择的是mailx,所以的关闭其他的邮件发送工具 service sendmailstop #关闭   chkconfig sendmailoff #禁止开机启 ...

  3. 教我徒弟Android开发入门(二)

    前言: 上一期实现了简单的QQ登录效果,这一期继续对上一期进行扩展 本期的知识点: Toast弹窗,三种方法实现按钮的点击事件监听 正文:   Toast弹窗其实很简单,在Android Studio ...

  4. JAVA受检异常和非受检异常举例

    受检异常和非受检异常(运行时异常)举例 RuntimeException(即非受检异常): RuntimeException在默认情况下会得到自动处理,所以通常用不着捕获RuntimeExceptio ...

  5. PAT 1003. Emergency 单源最短路

    思路:定义表示到达i的最短路径数量,表示到达i的最短径,表示最短路径到达i的最多人数,表示从i到j的距离, 表示i点的人数.每次从u去更新某个节点v的时候,考虑两种情况: 1.,说明到达v新的最短路径 ...

  6. JAVA在不确定具体 Annotation 类型时,获得注解参数

       package com.lzw.demo; @SpringBootApplication public class DemoApplication { public static void ma ...

  7. TCP/IP读书笔记(4) IPv4和IPv6 路由选择

    TCP/IP读书笔记(4) IPv4和IPv6 路由选择 网络层是位于链路层之上,TCP/IP模型中网络层的核心协议是IP协议(Internet protocol). 目前主流的IP协议是IPv4(I ...

  8. Http请求小结

    1.Http请求:get方式 public void httpGet(String url,Map<String,Object> map) { try { String joint = p ...

  9. Keras学习笔记

    Keras基于Tensorflow和Theano.作为一个更高级的框架,用其编写网络更加方便.具体流程为根据设想的网络结构,使用函数式模型API逐层构建网络即可,每一层的结构都是一个函数,上一层的输出 ...

  10. linux驱动---字符设备的注册register_chrdev说起

    首先我们在注册函数里面调用了register_chrdev(MEM_MAJOR,"mem",&memory_fops),向内核注册了一个字符设备. 第一个参数是主设备号,0 ...