Gym - 101954F Lighting 补题 数位dp

题意:给你一个长度为N的二进制数,问有多少种N为二进制和它相加,的出来的N位二进制数只有K个1

条件:1 N位二进制数相加,结果也是N位二进制数(溢出问题)

            2 结果得出的二进制数有K个1

            3 数据范围N(1~1000)K(0~N)

数位dp:dfs+记忆化数组

思路:直接暴力的dfs很容易想,就是从最低位一直加到最高位,然后还要考虑一下进位。如果到了最高位就看看是不是K个1,是的话种数++。但是这种很容易就超时了,每个位置都有2种可能,2的1000次方。

            会发现其实暴力的dfs,当加到相同位置 前面有相同种1 且进位也是一样的时候,后面要加的其实已经算过一次了。这时候开一个记忆化数组,时间复杂度就降到了1000x1000x2,就不会超时了。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int mod=1e9+7;
char s[1005];
int n,k;
long long int vis[1005][1005][2];//就是dp数组
//int vis[1005][1005];
long long int dfs(int pos,int one,int flag)//当前位置,1的种数,是否进位
{
    if(pos==-1)
    {
        if(one+flag==k)
        return 1;
        else
            return 0;
    }
    if(vis[pos][one][flag]!=-1)//后面已经被算过,直接拿来用
        return vis[pos][one][flag];
    long long int ans=0;
    if(s[pos]=='1'&&flag==0)
        ans+=(dfs(pos-1,one+1,0)%mod+dfs(pos-1,one,1)%mod)%mod;
    else if(s[pos]=='0'&&flag==0)
        ans+=(dfs(pos-1,one+1,0)%mod+dfs(pos-1,one,0)%mod)%mod;
    else if(s[pos]=='1'&&flag==1)
        ans+=(dfs(pos-1,one+1,1)%mod+dfs(pos-1,one,1)%mod)%mod;
    else if(s[pos]=='0'&&flag==1)
        ans+=(dfs(pos-1,one+1,0)%mod+dfs(pos-1,one,1)%mod)%mod;
    return vis[pos][one][flag]=ans%mod;
}
int main()
{
    memset(vis,-1,sizeof(vis));
    scanf("%d%d",&n,&k);
    scanf("%s",s);
    printf("%lld\n",dfs(strlen(s)-1,0,0));
    return 0;
}

总结:1 最难的地方在于记忆化数组怎么想,记忆化数组就是用来减少重复运算的。

你可能感兴趣的:(acm算法学习)