Campus Design HDU - 4804 (状压dp,轮廓线dp)

Problem Description
Nanjing University of Science and Technology is celebrating its 60th anniversary. In order to make room for student activities, to make the university a more pleasant place for learning, and to beautify the campus, the college administrator decided to start construction on an open space.
The designers measured the open space and come to a conclusion that the open space is a rectangle with a length of n meters and a width of m meters. Then they split the open space into n x m squares. To make it more beautiful, the designer decides to cover the open space with 1 x 1 bricks and 1 x 2 bricks, according to the following rules:

  1. All the bricks can be placed horizontally or vertically
  2. The vertexes of the bricks should be placed on integer lattice points
  3. The number of 1 x 1 bricks shouldn’t be less than C or more than D. The number of 1 x 2 bricks is unlimited.
  4. Some squares have a flowerbed on it, so it should not be covered by any brick. (We use 0 to represent a square with flowerbet and 1 to represent other squares)

Now the designers want to know how many ways are there to cover the open space, meeting the above requirements.

Input
There are several test cases, please process till EOF.
Each test case starts with a line containing four integers N(1 <= N <= 100), M(1 <= M <= 10), C, D(1 <= C <= D <= 20). Then following N lines, each being a string with the length of M. The string consists of ‘0’ and ‘1’ only, where ‘0’ means the square should not be covered by any brick, and ‘1’ otherwise.

Output
Please print one line per test case. Each line should contain an integers representing the answer to the problem (mod 109 + 7).

Sample Input
1 1 0 0
1
1 1 1 2
0
1 1 1 2
1
1 2 1 2
11
1 2 0 2
01
1 2 0 2
11
2 2 0 0
10
10
2 2 0 0
01
10
2 2 0 0
11
11
4 5 3 5
11111
11011
10101
11111

Sample Output
0
0
1
1
1
2
1
0
2
954

大致题意:告诉你一个由0,1组成的n*m的格子矩阵,0表示有障碍,1表示没有。让你用1*2和1*1这两种规格的砖块去铺满这个矩阵无障碍的部分,然后使用的1*1规格的砖块的数量有一定的限制。问有多少种方案。

思路:题目和poj2411很相似,只不过多了一些限制和要求。所以我们增加一维表示所放的1*1规格砖块的数量,那么dp[i][state][num]即表示第i行的状态为state,所放1*1规格砖块数为num时的方案数。然后还需要用滚动数组来优化内存,不然会mle。具体实现看下面代码。

代码如下

#include <cstdio>  
#include <cstring>  
#include <algorithm> 
#include <iostream> 
#include <vector>
using namespace std;  
#define ll long long   
const int mod=1e9+7; 
int n,m,c,d;
struct node 
{
    int state;
    int num;//记录到达状态state时所用的1*1规格的砖块的数量
};
ll dp[2][(1<<10)][21];//注意要开到21,因为1*1砖块数量范围是1到20
vector<node> Nex[(1<<10)];
int mp[100];

void dfs(int j,int state,int nex,int sum)//准备放第j列,将当前行状态state铺完后所能变换到的下一行的状态nex,此时已经用的1*1砖块的数量sum 
{
    if(j==m)//填完了m列 
    {
        node N;
        N.state=nex;
        N.num=sum;

        Nex[state].push_back(N);
        return;
    }
    if((1<<j)&state) dfs(j+1,state,nex,sum);//该位置不能填 
    else
    {
        //放1*1砖块
        dfs(j+1,state,nex,sum+1);
        //放1*2砖块
        dfs(j+1,state,nex|(1<<j),sum);//如果竖着放
        if(j+1<m&&(1<<(j+1)&state)==0)//如果能横着放 
        dfs(j+2,state,nex,sum); 
    }
}

int main ()  
{    
    while(scanf("%d%d%d%d",&n,&m,&c,&d)!=EOF)
    {   
        if(n==0)
        break;
        memset(dp,0,sizeof(dp));
        memset(mp,0,sizeof(mp)); 

        for(int i=0;i<(1<<m);i++)
        Nex[i].clear();

        for(int state=0;state<(1<<m);state++)//枚举当前行的所有状态
        dfs(0,state,0,0);//预处理出将当前行状态state铺满后所能达到的下一行的状态

        char str[15];
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            for(int j=0;j<m;j++)
            if(str[j]=='0')
            mp[i]+=(1<<j);//记录下第i行有障碍的格子
        }

        dp[0][0][0]=1;
        for(int i=0;i<n;i++)//枚举第i行 
        {
            memset(dp[(i+1)&1],0,sizeof(dp[(i+1)&1]));

            for(int state=0;state<(1<<m);state++) //枚举第i行放置的情况 
            for(int k=0;k<=20;k++)//枚举此时已经放置的1*1砖块的数量
            {
                if(dp[i&1][state][k])//如果此情况可达
                {

                    if(state&mp[i])//如果放置的位置有障碍物 
                        continue;
                    int state2=(state|mp[i]);//加上障碍物的位置,才是该行最终的状态
                    int len=Nex[state2].size();
                    for(int j=0;j<len;j++)//枚举铺完当前行所可以转移到的下一行的状态
                    {
                            int nex=Nex[state2][j].state;
                            int num=Nex[state2][j].num;
                            if(num+k<=d)//如果大于d的话就不用考虑了
                            dp[(i+1)&1][nex][num+k]=(dp[(i+1)&1][nex][num+k]+dp[i&1][state][k])%mod;
                    }   
                }   
            }
        }
        ll ans=0;
        for(int i=c;i<=d;i++)
        ans=(ans+dp[n&1][0][i])%mod;
        printf("%lld\n",ans);
    }   
    return 0;  
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值