您的位置:首页 > 编程语言 > C语言/C++

HDU 4804 Campus Design 轮廓线DP

2016-08-05 18:46 417 查看

原题

Description
Nanjing University of Scienceand Technology is celebrating its 60th anniversary. In order to make room forstudent activities, to make the university a more pleasant place for learning,and to beautify the campus,
the college administrator decided to startconstruction on an open space.

The designers measured the open space and come to a conclusion that the openspace is a rectangle with a length of n meters and a width of m meters. Thenthey split the open space into n x m squares. To make it more beautiful, thedesigner 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. Thenumber of 1 x 2 bricks is unlimited.

4. Some squares have a flowerbed on it, so it should not be covered by anybrick. (We use 0 to represent a square with flowerbet and 1 to represent othersquares)

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 Nlines, 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 testcase. Each line should contain an integers representing the answer to theproblem (mod 10
9 + 7).

大意

给一个大平面矩形格子,格子标号为0的不防砖,1的放砖,砖有1x1和1x2两种,其中1x1的只能使用C~D个,1x2无限制,输出方案数。

思路

    这是一道典型的轮廓线动态规划的贴砖块问题。

    状态上记录轮廓上每个格子的放置情况。这是因为每个放置最多影响到下一行和旁边一个格子,所以只要记录边缘的状态就可以了,记录轮廓而不是每行的状态是为了按格子递推的时候方便处理。没格只记录是否放,用01表示压缩成一个二进制数。如果记录放哪种砖,后面再把1*1的合并,则会出现叠加部分分不清的尴尬局面。

    递推的方式采取按一格一格的递推,每一格只修改当前所对应的状态位,如果横放1*2的则修改左边的状态位。虽然每一格后状态所表示的格子不同了,但这并不会让状态相互重叠。因为在每次递推到下一格的时候即滚动数组,状态也就统一到新轮廓下的各种方案数。

    这一类动态规划比插头的要简单许多,我觉得可以都归结为轮廓形,插头是轮廓状态记录是否有伸出去。推荐陈丹琦女神的经典论文《基于连通性状态压缩的动态规划问题》。

代码 C

#include <stdio.h>
#include <string.h>

#define MOD 1000000007

long long dp[2][1024][22];

int main()
{
int n, m, c, d, i, j, k, s, cur;
char squre[12];
while(scanf("%d%d%d%d", &n, &m, &c, &d) > 0)
{
memset(dp, 0, sizeof dp);
dp[cur=0][(1<<m)-1][0] = 1;
for(i=0; i<n; i++)
{
scanf("%s", squre);
for(j=0; j<m; j++)
{
cur ^= 1;
memset(dp[cur], 0, sizeof dp[cur]);
for(k=1<<m; k--;)
for(s=0; s<=d; s++)
{
if(squre[j] == '1')
{
if(k & 1 << j)
{
dp[cur][k][s+1] = (dp[cur][k][s+1] + dp[cur^1][k][s]) % MOD;
dp[cur][k^1<<j][s] = (dp[cur][k^1<<j][s] + dp[cur^1][k][s]) % MOD;
if(j && !(k&1<<j-1))
dp[cur][k^1<<j-1][s] = (dp[cur][k^1<<j-1][s] + dp[cur^1][k][s]) % MOD;
}
else
dp[cur][k^1<<j][s] = (dp[cur][k^1<<j][s] + dp[cur^1][k][s]) % MOD;
}
else if (k & 1 << j)
{
dp[cur][k][s] = (dp[cur][k][s] + dp[cur^1][k][s]) % MOD;
}
}
}
}
for(i=c, s=0; i<=d; i++)
s = (s + dp[cur][(1<<m)-1][i]) % MOD;
printf("%d\n", s);
}
return 0;
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息