hdu 4734 F(x) 数位dp

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4734

F(x)

Time Limit: 1000/500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2622    Accepted Submission(s): 969


Problem Description
For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A).
 

Input
The first line has a number T (T <= 10000) , indicating the number of test cases.
For each test case, there are two numbers A and B (0 <= A,B < 10 9)
 

Output
For every case,you should output "Case #t: " at first, without quotes. The  t is the case number starting from 1. Then output the answer.
 

Sample Input
  
  
3 0 100 1 10 5 100
 

Sample Output
  
  
Case #1: 1 Case #2: 2 Case #3: 13
 


题意:

问  0到b 之间有多少x符合  F(x)<=F(a)


做法:

数位dp

之前想到的状态,第二维sum  代表  这位之前 二进制数 加和是 sum, 这位及之后  有多少种数 二进制加和 + sum<=F(a)

然后超时了一发,加了个999的剪枝,200ms 过了。原因是里的dp意义和F(a)有关,所以每次要清空数组



后来看了别人的,发现原来很简单,正确的状态  sum 表示 这位及之后  有多少种数 二进制 加和<=sum

这里的dp值是固定的,每次不用清空,所以速度快很多。


第二种正确的:

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm> 
#include <bitset>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <map>
#include <time.h>
using namespace std;
const int inf = 0x3f3f3f3f;
typedef int LL;
const double pi = acos(-1.0);
#define eps 1e-8 

  
const int maxn=22;
int dig[maxn];
LL f[maxn][30000];//i位以后  二进制化后小于sum 的数字个数

LL dfs(int pos,int sum,int limit){
    if (pos<0) return sum>=0;
	if(sum<0) return 0;
    if (!limit&&f[pos][sum]!=-1) return f[pos][sum];
    LL res=0;
    int last=limit?dig[pos]:9;
    for (int i=0;i<=last;i++){
        res+=dfs(pos-1,sum-i*(1<<pos),limit&&(i==last));
    }
    if (!limit) f[pos][sum]=res;
    return res;
}

LL solve(LL n,LL a){

    int len=0;
	int fa=0;
    while (n){
        dig[len++]=n%10;
        n/=10; 
    }

	int tlen=0;
	while(a)
	{
		fa+=(a%10)*(1<<tlen);
		a/=10;
		tlen++;
	}

    return dfs(len-1,fa,1);
}


int main()
{
	int t;
	int cas=1;
	cin>>t;
	memset(f,-1,sizeof f);
	while(t--)
	{
		int a,b;
		cin>>a>>b;
		printf("Case #%d: ",cas++);
		cout<<solve(b,a)<<endl;
	}
	return 0;
}


第一种:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <malloc.h>
#include <ctype.h>
#include <math.h>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#include <stack>
#include <queue>
#include <vector>
#include <deque>
#include <set>
#include <map> 


typedef int LL;
const int maxn=10;
int dig[maxn];
LL f[maxn][5000]/* [TODO] */;//LL可能超内存

int lim;
int er[20];
int xian[20];
int jiu[20];
LL dfs(int pos ,int daxiao/* TODO */,int limit){
    if (pos<0) 
	{
		if(daxiao<=lim)
		return 1;/* TODO */;
		return 0;
	}

	
    if (!limit&&f[pos][daxiao]/* [TODO] */!=-1) 
		return f[pos][daxiao]/* [TODO] */;
	
	if(daxiao>lim)
		return 0;
	 
	
	if((!limit)&&pos>0&&(xian[pos]+daxiao)<=lim)
		return f[pos][daxiao]=jiu[pos+1]+1;
		


    LL res=0;
    int last=limit?dig[pos]:9;
    for (int i=0;i<=last;i++){
        res+=dfs(pos-1,daxiao+i*er[pos]/* TODO */,limit&&(i==last));
    }
    if (!limit) f[pos][daxiao]/* [TODO] */=res;
	 
    return res;
}

LL solve(LL n){
	memset(f,-1,sizeof f);
    int len=0;
    while (n){
        dig[len++]=n%10;
        n/=10;
    }




    return dfs(len-1,/* TODO */0,1);
}

LL ji(LL n)
{ 


	int len=0;
    while (n){
        dig[len++]=n%10;
        n/=10;
    }

	LL res=0;
	for(int i=len-1;i>=0;i--)
	{
		res+=dig[i]*er[i];
	}
	return res;
}


int main()
{
		

	int res=0;

	res=0;
	for(int i=0;i<15;i++)
	{
		
		jiu[i]=res;
		res+=(res+1)*9;
	}
	jiu[0]=0;


	er[0]=1;
	for(int i=1;i<15;i++)
		er[i]=er[i-1]*2;

	res=0;
	for(int i=0;i<15;i++)
	{
		res+=9*er[i];
		xian[i]=res;
	} 



	int cas=1;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		
		lim=ji(a);
		//cout<<lim<<endl;
		int ans;
		/*
		if(b<=a)
			ans=b+1;
		else*/
			ans=solve(b); 
		
			/*
			int num=0;
			for(int i=0;i<=b;i++)
			{
				if(ji(i)<=lim)
					num++;
			}
			cout<<"num  "<<num<<endl;*/
			

		printf("Case #%d: %d\n",cas++,ans);
	}
	return 0;

}
/*
9999999
99999999 99999999
*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值