• 【上海交大oj】畅畅的牙签袋(状态压缩dp)


    题目描述

    畅畅说:“你们都说窝脑子瓦特了,我看你们才是脑子瓦特嘞- -”

    为了阻挠我们再次揭露他的无chǐ,畅畅妄图破坏我们的显示器,他拿出了自己收集的牙签包装袋,其大小是1×2的矩形,他想用密铺的方式把显示器全部遮挡住。显示器大小是W×H的矩形,畅畅把包装袋背面涂上了胶水,开始一块一块粘到显示器上,要求不能有包装袋重叠,也不能有显示器的某一部分没被遮挡,包装袋的放置只有两种情况:横放和竖放。

    畅畅为自己的周密计划洋洋得意之时,脑子又瓦特了,他想让你帮他算算针对每台显示器一共有多少种不同的密铺方式。

    说明

    1. 认为显示器是有方向的,即对称的密铺方式要算重复计数,例如 2×2 的显示器密铺方案有两种,一种是2个包装袋都横放,另一种是两个包装袋都竖放。

    2. 不能密铺的则输出0。

    3. 牙签包装袋足够用。

    输入格式

    输入包括若干行,每行有两个整数Wi和Hi,表示这台显示器大小,输入数据以Wi=Hi=0作为结束,每台显示器大小 1<=Hi,Wi<=11。

    输出格式

    针对每行输入,每次输出一个整数表示对该显示器密铺的方案总数。Wi=Hi=0时不需要输出。

    Sample Input

    1 2
    1 3
    1 4
    2 2
    2 3
    2 4
    2 11
    4 11
    0 0
    

    Sample Output

    1
    0
    1
    2
    3
    5
    144
    51205
    

    提示

    畅畅说如果Wi×Hi是奇数的话可行的方案数就是0了。

    畅畅思来想去,认为以行为单位来一行行填充,同时用状态压缩的方式表示每一行的填充状态,0表示空1表示已填充,如一个长为3的行有000,001,010,011,100,101,110,111这样几种状态表示。

    畅畅觉得动态规划太难的话,只有暴力搜索了。

    畅畅说30%测试点只有一台显示器,70%测试点只有不超过十台显示器,100%测试点只有不超过121台显示器

    畅畅说答案很小的,long long就够了


    铺砖块问题。每一行为一个阶段,最多有2^11种状态,那么枚举这一行的状态就好了,在这一行的状态确定的情况下,枚举上一行的状态,检查是否兼容(若上一行为0,表示没有铺,则这一行对应位置必须为1,若上一行为1,这一行可以为0也可以为1,但要注意的是,若为1,说明两行的这个位置都是平铺,否则必有其中一个为零(下一行来铺),那么由于是平铺,下一个位置的两行都为1,否则状态不兼容),若兼容则加上对应的数目。

    复杂度为h*(2^w)*(2^w)。也可以一个点一个点动归,这个还没实现过,暂时不表。

    代码:

     1 #include <iostream>
     2 #include <cstring>
     3 using namespace std;
     4  
     5 int w,h;
     6 long long dp[11][1<<11];
     7 long long first_line(int n)
     8 {
     9     int t = 1;
    10     for (int i = 1;i <= w;++i,t <<= 1)
    11     {
    12         if (t & n)
    13         {
    14             if (i==w || !(n&(t<<1))) return 0;
    15             else
    16             {
    17                 i++;
    18                 t <<= 1;
    19             }
    20         }
    21     }
    22     return 1;
    23 }
    24 bool match(int n,int l)
    25 {
    26     int t = 1;
    27     for (int i = 1;i <= w;++i,t<<=1)
    28     {
    29         if (t & n)
    30         {
    31             if (t & l) //两行都为1 
    32             {
    33                 if (i==w || !((n & (t<<1)) && (l & (t<<1))) ) return 0;
    34                 else
    35                 {
    36                     i++;t<<=1; 
    37                 }
    38             }
    39         }
    40         else if (!(t & l)) return 0;
    41     }
    42     return 1;
    43 }
    44 int main(){
    45  
    46     while (1)
    47     {
    48         cin>>w>>h;
    49         if (h==0 && w==0) break;
    50         if ((h*w)&1) //边长为奇数 
    51         {
    52             cout<<0<<endl;
    53             continue;
    54         }
    55         if (h<w) //减少复杂度 
    56         {
    57             h = h^w;
    58             w = h^w;
    59             h = h^w;
    60         }
    61         memset(dp,0,sizeof(dp));
    62         for (int i = 0;i < 1<<w;++i) dp[0][i] = first_line(i); //initialize
    63         for (int i = 1;i < h;++i)
    64             for (int j = 0;j < 1<<w;++j) //当前行状态枚举 
    65                 for (int k = 0;k < 1<<w;++k) //前一行状态枚举 
    66                 {
    67                     if (match(j,k)) dp[i][j] += dp[i-1][k];
    68                 }
    69         cout<<dp[h-1][(1<<w)-1]<<endl;
    70     }
    71  
    72     return 0;
    73 }
    View Code
  • 相关阅读:
    makefile中宏定义
    make的静态模式
    makefile中两重if判断
    定义命令包
    嵌套执行make
    AcWing 1014. 登山
    AcWing 482. 合唱队形
    AcWing 1027. 方格取数
    AcWing 1016. 最大上升子序列和
    AcWing 187. 导弹防御系统
  • 原文地址:https://www.cnblogs.com/wenma/p/4655850.html
Copyright © 2020-2023  润新知