两个数位DP,记忆化搜索版(HDU 3652 HDU 3709)

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

HDU 3652

http://acm.hdu.edu.cn/showproblem.php?pid=3652

出现13,而且能被13整除。

加一维表示当前的余数。那么在后面加一位,余数被为(mod*10+i)%13。

递归的貌似好些点,不过应该在效率方面略差。细节处理貌似方便点。

详见代码注释


#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
#define inf 1<<29
#define MOD 9973
#define LL long long
#define eps 1e-7
#define zero(a) fabs(a)<eps
#define equal(a,b) zero(a-b)
using namespace std;
int dp[10][13][3];
//dp[i][j][k]表示i位数,对13的余数是j
//k为0表示没有出现13
//k为1表示没有出现13,但是首位为3
//k为2表示出现13
int bit[15],len;
//分别表示当前考虑的位置,前一个数字,当前余数,是否有限制,是否已经出现13
int dfs(int pos,int pre,int mod,bool limit,bool flag){
	if(pos<=0) return flag&&(mod==0);  //如果已经出现了13,而且余数为0,返回1,否则为0
	if(!limit&&flag&&dp[pos][mod][0]!=-1)  return  dp[pos][mod][0];  //没有限制而且之前已经出现13,那么后面就随意
	if(!limit&&!flag&&pre!=1&&dp[pos][mod][2]!=-1) return dp[pos][mod][2];  
	if(!limit&&!flag&&pre==1&&dp[pos][mod][1]!=-1) return dp[pos][mod][1];  //之前没有13,但是末位是1,那么后面的高位可以是3
	int end=(limit?bit[pos]:9);
	int ret=0;
	for(int i=0;i<=end;i++)
		ret+=dfs(pos-1,i,(mod*10+i)%13,limit&&(i==end),flag||(pre==1&&i==3));
	if(!limit){
		if(pre==1&&!flag) dp[pos][mod][1]=ret;
		if(pre!=1&&!flag) dp[pos][mod][2]=ret;
		if(flag) dp[pos][mod][0]=ret;
	}
	return ret;
}
int slove(int n){	
	len=0;
	while(n){
		bit[++len]=n%10;
		n/=10;
	}
	return dfs(len,0,0,true,false);
}
int main(){
	int n;
	memset(dp,-1,sizeof(dp));
	while(scanf("%d",&n)!=EOF)
		printf("%d\n",slove(n));
	return 0;
}

HDU 3709 

http://acm.hdu.edu.cn/showproblem.php?pid=3709

平衡数,枚举支点,然后其它的类似。加一维表示当前的力矩,注意当力矩为负时,就要返回,否则会出现下标为负,也算是个剪枝。

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
#define inf 1<<29
#define MOD 9973
#define LL long long
#define eps 1e-7
#define zero(a) fabs(a)<eps
#define equal(a,b) zero(a-b)
using namespace std;
LL dp[20][20][2005];
//dp[i][j][k]表示考虑i位数字,支点为j,力矩和为k
int bit[20],len;
LL dfs(int pos,int central,int pre,bool limit){
	if(pos<=0) return pre==0;    //
	if(pre<0)  return 0;   //当前力矩为负,剪枝
	if(!limit&&dp[pos][central][pre]!=-1) return dp[pos][central][pre];
	int end=limit?bit[pos]:9;
	LL ret=0;
	for(int i=0;i<=end;i++)
		ret+=dfs(pos-1,central,pre+i*(pos-central),limit&&(i==end));
	if(!limit)
		dp[pos][central][pre]=ret;
	return ret;
}
LL slove(LL n){
	len=0;
	while(n){
		bit[++len]=n%10;
		n/=10;
	}
	LL ans=0;
	for(int i=1;i<=len;i++)
		ans+=dfs(len,i,0,true);
	return ans-len+1;   //除掉全0的情况,00,0000满足条件,但是重复了
}
int main(){
	LL l,r;
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%I64d%I64d",&l,&r);
		memset(dp,-1,sizeof(dp));
		printf("%I64d\n",slove(r)-slove(l-1));
	}
	return 0;
}



你可能感兴趣的:(两个数位DP,记忆化搜索版(HDU 3652 HDU 3709))