数位dp专辑

1.题目链接:codeforces 55D

【题意】一个数能被它每一位的数字整除(0除外)则为beautiful number ,问[l,r]之间有多少个这样的数

【思路】考虑到每一位的数字只能是1,2,,9,最小公倍数为2520

设数w=x*2520+y,每位数字的最小公倍数为mul,则w%mul=(x*2520+y)%mul=x*2520%mul+y%mul=y%mul,只需要记录每一步过后该数对2520取模即可。

dp[len][y][mul]表示处理到第len位时数对2520的模,及当前所有数字的最小公倍数。可开到dp[20][2520][2520]

由于数字的最小公倍数是有限的,可将第三维2520进一步优化,预处理求出2520所有的约数。

优化为dp[20][2520[60]


/***********************************************************************
    > File Name: cf55d.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月06日 星期一 15时27分54秒
 ************************************************************************/

#include
#include
#include
#define ll long long 
using namespace std;
ll dp[20][2520][60];
int num[20];
int v[60],cnt;
int to[2600];
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
int lcm(int a,int b)
{
	if(b==0)
		return a;
	return a*b/gcd(a,b);
}
ll dfs(int p,int y,int multi,int flag)
{
//cout<>a>>b;
		cout<


2.题目链接:hdu4352

【题意】定义一个数的值为其数字最长上升子序列的长度,例如3746,最长上升序列为 3,4,6,值为3.求[l,r]个中值为k的数有多少个。

【思路】好题。。忍不住看了题解。。求最长上升子序列时,我们需要维护一个序列,表示长度为1的最后一个数的最小值,长度为2的最后一个数的最小值,长度为3的最后一个的最小值。。。这个序列是单调递增的,我们只需要记录下这个序列即可,只有0-9  10个数字,状态压缩!

/*************************************************************************
    > File Name: hdu4352.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月09日 星期四 20时19分09秒
 ************************************************************************/

#include
#include
#include
#define ll long long
using namespace std;

ll dp[20][1<<10][11];
int num[20];

int k;
int getlen(int state)
{
	int res=0;
	for(int i=0;i<10;i++)
		res+=(state&(1<=0;i--)
	{
		if(state&(1<=v)
				return state^(1<len)return dp[p][state][len]=0;
	ll res=0;
	int u=flag?num[p]:9;
	for(int i=0;i<=u;i++)
	{
		int now=update(state,i);
		res+=dfs(p-1,now,len,flag&&i==num[p]);
	}

//	cout<

3.题目链接: hdu2089

【题意】含4或62的数为不吉利数,求[l,r]中有多少个吉利的数

【思路】水题。。直接写

/*************************************************************************
    > File Name: hdu2089.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月06日 星期一 15时58分40秒
 ************************************************************************/

#include
#include
#include
using namespace std;

int num[10];
int len;
int dp[10][2];
int dfs(int p,int flag,int flag2)
{
	if(p==-1)
		return 1;
	if(!flag&&dp[p][flag2]!=-1)
		return dp[p][flag2];
	int u=flag?num[p]:9;
	int res=0;
	for(int i=0;i<=u;i++)
	{
		if(i==2&&!flag2)continue;
		if(i==4)continue;
		res+=dfs(p-1,flag&&i==num[p],i!=6);
	}
	return dp[p][flag2]=res;
}
int work(int x)
{
	memset(dp,-1,sizeof(dp));
	int len=0;
	while(x)
	{
		num[len++]=x%10;
		x/=10;
	}
	return dfs(len-1,1,1);
}
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		if(n==0&&m==0)
			break;
		printf("%d\n",work(m)-work(n-1));
	}
	return 0;
}

4.题目链接:hdu3555

【题意】跟上面一题差不多,求[1,x]中含49的有多少个

   【思路】同样水题,记录前一个数字是不是4

/*************************************************************************
    > File Name: hdu3555.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月06日 星期一 16时27分38秒
 ************************************************************************/
#include
#include
#include
#define ull  unsigned long long 
#define ll long long
using namespace std;
int num[25];
ll A[20];
long long dp[25][2];
long long dfs(int p,int flag,int flag4,int ok)
{
//	cout<


5.题目链接:poj3252

【题意】将数化为二进制,0的个数不比1少,求[l,r]中这样的数有多少

【思路】将数转化为2进制,再进行dp,保存当前0,1的个数,注意开头取0的情况

/*************************************************************************
    > File Name: poj3252.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月06日 星期一 19时23分03秒
 ************************************************************************/

#include
#include
#include
using namespace std;

int dp[35][35][35][2];
int num[35];

int dfs(int p,int num0,int num1,int flag)
{
//	cout<=num1;
	if(dp[p][num0][num1][flag]!=-1)
		return dp[p][num0][num1][flag];
	int u=flag?num[p]:1;
	int res=0;
	for(int i=0;i<=u;i++)
	{
		res+=dfs(p-1,num1?num0+!i:0,num1+i,flag&&i==num[p]);
	}
//	cout<<"ans "<=0;j--)
//		cout<

6.题目链接:hdu3709

【题意】取一个数中的某一数字为支点,左右距离*数字之和相等即为平衡数,例如4139,取3为支点,4*2+1*1=9*1,4139为平衡数求[l,r]间有多少个平衡数

【思路】对于一个数,若其为平衡数,则平衡点必唯一。设取x 点为支点,左侧加权和为Lx,数字和为SUMl;右侧加权和为Rx,数字和为SUMr.该数字为N x,则支点右移一位左侧加权和为Lx+SUMl+Nx,右侧加权和为Rx-SUMr。

观察原左右侧加权和之差,Lx-Rx,现加权和之差,Lx-Rx-(SUMl+Nx+SUMr)=Lx-Rx-sum(所有数字之和)。

发现,对于每一个支点,求得的差都是不一样的,因此平衡点必唯一。若存在平衡点,则Lx-Rx=0,取任意一点为支点,差值必然是sum的倍数!若差值为sum的倍数则必然存在平衡点,最左侧为支点差值<=0,最右侧为支点差值>=0,中间必有平衡点!以最右侧为支点,算出的差值为sum的倍数则该数是平衡数

/*************************************************************************
    > File Name: hdu3709.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月06日 星期一 20时07分21秒
 ************************************************************************/

#include
#include
#include
#define ll long long 
using namespace std;
int num[25];
ll dp[2000][180][20];
ll dfs(int p,int value,int sum,int flag)
{
	//cout<>t;
	while(t--)
	{
		long long a,b;
		cin>>a>>b;
		if(a>b)
		{
			int tm=b;
			b=a;
			a=tm;
		}
		cout<

7.题目链接:hdu3652

【题意】数中出现13,[1,n]中有多少个这样的数

【思路】。。。

/*************************************************************************
    > File Name: hdu3652.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月07日 星期二 00时18分32秒
 ************************************************************************/

#include
#include
#include
using namespace std;

int num[15];
int dp[12][13][2][2];
int dfs(int p,int yu,int flag1,int flag13,int flag)
{
	if(p==-1)
		return yu==0&&flag13;
	if(!flag&&dp[p][yu][flag1][flag13]!=-1)//flag13也可单列出来,减小一维
		return dp[p][yu][flag1][flag13];
	int u=flag?num[p]:9;
	int res=0;
	for(int i=0;i<=u;i++)
	{
		res+=dfs(p-1,(yu*10+i)%13,i==1,flag13||(flag1&&i==3),flag&&i==num[p]);
	}
//	cout<

8.题目链接:hdu4734

【题意】函数  F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1求[0,b]中f(i)比f(A)小的个数。

【思路】f最大为9*2^9.....+9*2^0=9*(2^10-1)<10*2^10,dp[len][sum]表示剩下长度为len,f值比sum小或相等的个数。奇怪的题目。。。

/*************************************************************************
    > File Name: hdu4734.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月07日 星期二 00时18分32秒
 ************************************************************************/

#include
#include
#include
using namespace std;
int num[12];
int dp[12][5120];
int fa;
int f(int x)
{
	int res=0;
	int v=1;
	while(x)
	{
		res=res+x%10*v;
		x/=10;
		v*=2;
	}
	return res;
}
int A[10];
int dfs(int p,int sum,int flag)
{
//	cout<=0;
	if(sum<0)
		return 0;
	if(!flag&&dp[p][sum]!=-1)
		return dp[p][sum];
	int u=flag?num[p]:9;
	int res=0;
	for(int i=0;i<=u;i++)
	{
		res+=dfs(p-1,sum-i*A[p],flag&&i==num[p]);
	}
//	cout<


9.题目链接:hdu4507

【思路】主要还是推平方和的公式  ∑(x+ai)^2=n*x^2+2*x*∑ai+∑ai^2,需要得到子问题的和及平方和及个数。细节地方一定要注意,个数为0与和为0是不一样的,和为0还有可能存在符合条件的数。求个数的时候一定要注意不要取模。。然而取模居然也能AC。。

/*************************************************************************
    > File Name: hdu4507.cpp
    > Author: wanghao
    > Mail: [email protected] 
    > Created Time: 2015年07月07日 星期二 20时07分53秒
 ************************************************************************/

#include
#include
#include
#define ll long long
using namespace std;
int mod=1000000007;
ll dp[20][7][7][3];

ll A[20];
int num[20];
void dfs(int p,int sum7,int value7,int flag,ll *sum,ll *sum2,ll *sumn)
{
	if(p==-1)
	{
		*sum=*sum2=0;
		if(sum7==0||value7==0)
			*sumn=0;
		else *sumn=1;
		return ;
	}
	if(!flag&&dp[p][sum7][value7][2]!=-1)
	{
		*sum=dp[p][sum7][value7][0];
		*sum2=dp[p][sum7][value7][1];
		*sumn=dp[p][sum7][value7][2];
		return ;
	}
 	int u=flag?num[p]:9;
	*sumn=*sum=*sum2=0;
	int ok=0;
	for(int i=0;i<=u;i++)
	{
		if(i==7)continue;
		ll a,b,c;
		dfs(p-1,(sum7+i)%7,(value7*10+i)%7,flag&&i==num[p],&a,&b,&c);
		if(c==0)//不判也能ac...但不知道为毛
			continue;
		ok=1;
		*sum2=(*sum2+c%mod*i*A[p]%mod*i*A[p]%mod+b+2*i*A[p]%mod*a%mod)%mod;
		*sum=(*sum +c%mod*i*A[p]+a)%mod;
		*sumn=*sumn+c;//别取模。。但取模也能a
	}
//	cout<<"df "<>a>>b;
		cout<<(work(b)-work(a-1)+mod)%mod<<'\n';
	}
	return 0;
}

PS: 就是模板题( ⊙ o ⊙ )啊!

你可能感兴趣的:(动态规划,dp)