【JZOJ】【DP】好数

这篇博客介绍了一种使用动态规划(DP)解决寻找二进制中具有三个连续相同数字的好数问题的方法。通过求解坏数(不满足条件的数)的数量,博主详细阐述了状态转移方程,并提供了思路分析,包括如何处理边界情况和求解特定范围内的好数个数。

DescriptionDescriptionDescription

某一个数的二进制中有三个连续相同的数为好数
例如
8的二进制为1000
有三个连续的0
7的二进制为111
有三个连续的1
给定n与m
求在n与m之间的好数个数

InputInputInput

一行,n与m

OutputOutputOutput

一行,这样的数的个数

SampleInputSample InputSampleInput#1
0 16
SampleInputSample InputSampleInput#2
17 100
SampleInputSample InputSampleInput#3
2000000000 2100000000
SampleInputSample InputSampleInput#4
2 5
SampleOutputSample OutputSampleOutput#1
5
SampleOutputSample OutputSampleOutput#2
49
SampleOutputSample OutputSampleOutput#3
100000001
SampleOutputSample OutputSampleOutput#4
0

思路

数位DP
题目中求好数
但我们也可以求坏数
我们设F[i][0][0]F[i][0][0]F[i][0][0]
为第iii位(从右往左)为0
与第i−1i-1i1位(从右往左)为0
的二进制的坏数的个数
二进制:
10−1110-111011F[2][1][0]+F[2][1][1]F[2][1][0]+F[2][1][1]F[2][1][0]+F[2][1][1]
100−111100-111100111F[3][1][0]+F[3][1][1]F[3][1][0]+F[3][1][1]F[3][1][0]+F[3][1][1]
100−111100-111100111F[4][1][0]+F[4][1][1]F[4][1][0]+F[4][1][1]F[4][1][0]+F[4][1][1]
100−111100-111100111F[5][1][0]+F[5][1][1]F[5][1][0]+F[5][1][1]F[5][1][0]+F[5][1][1]
转移:
F[i][1][0]=F[i−1][0][0]+F[i−1][0][1]F[i][1][0]=F[i-1][0][0]+F[i-1][0][1]F[i][1][0]=F[i1][0][0]+F[i1][0][1]
F[i][0][1]=F[i−1][1][1]+F[i−1][1][0]F[i][0][1]=F[i-1][1][1]+F[i-1][1][0]F[i][0][1]=F[i1][1][1]+F[i1][1][0]
如果是两个0或1
就只能这样
F[i][1][1]=F[i−1][1][0]F[i][1][1]=F[i-1][1][0]F[i][1][1]=F[i1][1][0]
F[i][0][0]=F[i−1][0][1]F[i][0][0]=F[i-1][0][1]F[i][0][0]=F[i1][0][1]
因为如果还是F[i−1][0][0]F[i-1][0][0]F[i1][0][0]F[i−1][1][1]F[i-1][1][1]F[i1][1][1]
那就是好数了
假设mmm为100
二进制为10101010
按照上面的方法
我们能求出
1−111111111-11111111111111111中的坏数
那10000000-10101010中的坏数怎么求呢
我们可以从左往右
除去第一位的1
一个个变0求
例如10101010
除去第一位的1
第二位的1变0
就变成了100???
这样就是F[7][0][0]F[7][0][0]F[7][0][0]
第三位的1变0
就变成了10100???
这样就是F[5][0][0]F[5][0][0]F[5][0][0]
接着第四位
1010100?
F[3][0][0]F[3][0][0]F[3][0][0]
三个加起来就是1000000-10101010中的坏数
最后在看一下m是否为坏数
就能求出0到m之间的坏数了
(n-1)同理

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
ll F[125][2][2];
ll Ans,Sum,len,k,n,m;
string sm;
ll Change(ll t)
{
	if(t<=0)return 0;
	Ans=0,k=t,len=0;
	sm="";
	while(k)//求t的二进制
	{
		char s=k%2+48;
		sm=s+sm,k/=2;
	}
	len=sm.length();
	for(int i=2;i<len;++i)//求t的二进制长度-1位的坏数
		Ans+=F[i][1][0]+F[i][1][1];
	if(t>0)Ans++;//0也是坏数
	k=0;//求t的二进制长度位的坏数
	while(++k<=len)
		if(sm[k]=='1')
		{
			sm[k]='0';
			int l=1;char r=sm[0];
			bool B=0;
			for(int i=1;i<=k;++i)
				if(sm[i]==r)
				{
					l++;
					if(l==3){B=1;break;}; 
				}
				else l=1,r=sm[i];
			if(B==0)
				Ans+=F[len-k+1][sm[k-1]-48][0];
			sm[k]='1';//记得变回来
		}
	int l=1;char r=sm[1];
	bool B=0;
	for(int i=2;i<=len;++i)//判断m是否为坏数
		if(sm[i]==r)
		{
			l++;
			if(l==3){B=1;break;}; 
		}
		else l=1,r=sm[i];
	if(!B)Ans++;
	return t-Ans;
}
int main()
{
	freopen("d.in","r",stdin);
	freopen("d.out","w",stdout);
	scanf("%d%d",&n,&m);
	F[2][1][0]=F[2][1][1]=
	F[2][0][0]=F[2][0][1]=1;//初值
	for(int i=3;i<=31;++i)
	{
		F[i][0][0]=F[i-1][0][1];
		F[i][1][0]=F[i-1][0][0]+F[i-1][0][1];
		F[i][1][1]=F[i-1][1][0];
		F[i][0][1]=F[i-1][1][1]+F[i-1][1][0];
	}
	Sum=Change(m)-Change(n-1);//前缀和
	printf("%d",Sum);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值