一. 至少转最多

处理何种问题:有m 种类型的物品(每种类型的物品个数可视为无限),从中选出n个物品,求物品s至少有一次连续选k件的方案数。

例题:抛硬币,抛n次,求正面至少连续出现k次的种数。抛3次,连续出现2次正面的种数是3(110,011,111)。

 

性能:时间复杂度为O(nm),空间复杂度也是(nm)。

 

原理:设事件A={最多选n个s物品连续},B={最多选k-1个s物品连续},

A-  B={至少选k件s件物品连续}。 dp[i][j] 表示第i次选择时选择j 物品时满足

条件的方案数,注意:dp[i][j]求的是最多选s个物品连续的情况;

l  对于非s物品,对整个没有影响,所以dp[i][~s]=sum[i-1];

l  对于s物品:

n  当i<=u 时(n,k-1都为u),dp[i][s]=sum[i-1](因为肯定没超过n)。

n  当i==u+1时,dp[i][s]=sum[i-1]-1(这种情况下,只有一种可能会超过n)。

n  当i>u+1时,dp[i][s]=sum[i-1]-dp[i-u-1][~s](此时要减去i前面已经出现连续u个s的情况,即从i-u到i-1这一段都是s的情况,其个数就是

dp[i-u-1][~s]之和,为什么没有减掉dp[i-u-1][s],因为当i-u-1为s时,所有i-1都肯定是~s了)唉,dp就是在人脑里跑递归啊。

 

实现步骤:至少转最多,用DP实现。

 

备注:此题涉及面比较广,可作为入门dp的练习题,且拓展性也不错,曾遇到一个至少+一个最多。写此题目的不是为了做模板用,只为加深dp理解。

 

输入样例解释

10 3 2 5 //n m s k

 

输出样

1053 //方案数

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,s,k,ans;
const int mod=1e9+7;
long long sum[1000010];
long long dp[1000010][10];

long long f(int u)
{
    for(int i=1; i<=n; ++i)
    {
        for(int j=0;j<m;++j)
        {
            if(j!=s)
                dp[i][j]=sum[i-1];
        }
        if(i<=u)
            dp[i][s]=sum[i-1];
        else if(i==u+1)
            dp[i][s]=sum[i-1]-1;
        else if(i>u+1)
        {
            dp[i][s]=sum[i-1];
            for(int j=0;j<m;++j)
            {
                if(j!=s)
                    dp[i][s]-=dp[i-u-1][j];
            }

        }

        sum[i]=0;
        for(int j=0;j<m;++j)
        {
            sum[i]+=dp[i][j];
        }
    }
    return sum[n];
}

int main()
{
    while(~scanf("%d%d%d%d",&n,&m,&s,&k))
    {
        sum[0]=1;
        printf("%lld\n",f(n)-f(k-1));
    }
    return 0;
}

/*
//暴力验证代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,s,k,ans;
int arr[100];

void dfs(int ctor)
{
    if(ctor>n)
    {
        int sum=0,flag=0;
        for(int i=1;i<=n;++i)
        {
            if(arr[i]==s)
            {
                if(arr[i]!=arr[i-1])
                    sum=1;
                else if(arr[i]==arr[i-1])
                    ++sum;
            }
            else
                sum=0;
            if(sum>=k)
                flag=1;
        }
        if(flag)
        {

            ++ans;
            printf("%d:",ans);
            for(int i=1;i<=n;++i)
                printf("%d",arr[i]);
            printf("\n");
        }

    }
    else
    {
        for(int i=0;i<m;++i)
        {
            arr[ctor]=i;
            dfs(ctor+1);
        }
    }
}


int main()
{
    while(~scanf("%d%d%d%d",&n,&m,&s,&k))
    {
        ans=0;
        arr[0]=-1;
        dfs(1);
        cout<<ans<<endl;
    }
    return 0;
}
*/

  

posted @ 2018-08-14 17:01  逃往火星的猫  阅读(132)  评论(0编辑  收藏  举报