Hrbust 1846 方格涂色【dp+递推思维】好题!好题!好题!

探讨一种经典的动态规划问题——方格涂色。在一条由N个方格组成的直线上,每个方格可涂成红或蓝两色,但必须确保至少连续M个方格为红色。本文详细解析如何利用动态规划求解该问题的有效方案数量。

方格涂色
Time Limit: 1000 MSMemory Limit: 32768 K
Total Submit: 31(14 users)Total Accepted: 12(10 users)Rating: Special Judge: No
Description

有一条有n个方格的直线,方格标号从1到N,将所有方格涂成两种颜色红或者蓝。

并且要求至少连续M个方格是红色。问有多少种方案数。

Input
有多组测试数据
对于每组测试数据,输入两个整数N和M(0<N,M<=100000)。
Output
方案数对1000000007取模。
Sample Input
4 3
Sample Output
3

思路:


1、统计计数问题,我们考虑dp,设定dp【i】表示长度为i的合法方案数。

那么考虑其状态转移方程:

①我们首先考虑递推式,那么对应可以先手写样例来递推(O代表红色,X代表蓝色):

2 2:

OO

3 2(我们可以在2 2的基础上,后边加一个X,和一个O):

OOX

OOO

这里就能够构成两种情况,那么显然我们还可以在OO的最前边加O或者是X

XOO

OOO

很明显,在前边加O的方案重复了,那么此时只有三种合法情况:

OOX

OOO

XOO

我们接下来考虑4 2(我们还可以在3 2的基础上,后边加一个X,和一个O):

OOXX

OOOX

XOOX

OOXO

OOOO

XOOO

这里就能够构成六种情况,那么显然我们可以在OO的最前边加OO,OX,XX,XO:

OOOO

OXOO

XXOO

XOOO

其中重复了两种情况,那么去除之后剩下八种合法情况:

OOXX

OOOX

XOOX

OOXO

OOOO

XOOO

OXOO

XXOO


2、通过分析和列举我们能够知道这样一个问题:

我们在手写样例的时候,我们可以在上一个情况的基础上,在序列末尾加上一个蓝色方块或者是红色方块。

然后再在M个红色方块前边加上2的平方次种情况,再去重即可。

再通过大量的分析能够得到一个结论,在连续个M红色方块前边加上2的平方次种的情况的时候去重之后,直接相连的那个位子一定是X

比如:

OOO,如果我们假如想要得到一个长度为5的结果:

XOOOO

XXOOO

OOOOO

OXOOO

很显然,OOOOO,XOOOO两种情况是一定重复过的。


3、那么我们考虑上述过程,可以大概写出来一个框架:

dp【i】=dp【i-1】*2+2^(i-m-1)【减一的目的就是在连续的O前边,先加上一个X,再考虑前边的部分】-?【这里?表示还需要对一部分进行去重】

那么我们接下来再考虑从4 2递推到5 2:

(4 2的合法情况:)

OOXX

OOOX

XOOX

OOXO

OOOO

XOOO

OXOO

XXOO

我们知道,去重的部分,肯定是在前边加东西的时候加重复的部分要去除,那么我们考虑加上:

OOXOO

OXXOO

XOXOO

XXXOO

这四种是可以加进来的,考虑哪个情况是会重复的呢?

显然是OOXOO的情况重复了。那么考虑长度为2的时候dp值:dp【2】=1;我们肯定有方案是冲dp【2】一直递推过来的,就是说最开头的两个O一定会通过递推得到过OOXOO,所以这个?就是dp【i-m-1】;


4、那么状态转移方程就是:

dp【i】=①dp【i-1】*2+②2^(i-m-1)-③dp【i-m-1】;

①直接在长度为i-1的所有方案数的基础上,后边加上一个O或者X,所以是乘以2.

②接下来在连续M个O前边加上N-M个情况的时候,肯定紧挨着的那个要放X,所以是考虑2^(i-m-1)种情况。

③再接下来对于2^(i-m-1)种情况进行去重,去重部分就是dp【i-m-1】;因为肯定有dp【i-m-1】这些情况递推出来一系列已有情况,所以要去重。


5、这个题真的很不错啊!


Ac代码:

#include<stdio.h>
#include<string.h>
using namespace std;
#define ll long long int
#define mod 1000000007
ll dp[150000];
ll b[150000];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<=n;i++)
        {
            if(i==0)b[i]=1;
            else
            {
                b[i]=(b[i-1]*2)%1000000007;
            }
        }
        memset(dp,0,sizeof(dp));
        dp[m]=1;
        for(int i=m+1;i<=n;i++)
        {
            dp[i]=((dp[i-1]*2)+b[i-m-1]-dp[i-m-1]+mod)%mod;
        }
        printf("%d\n",(dp[n]+mod)%mod);
    }
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值