HDU - 3555 和 HDU - 3652

数位DP真的是套路题,今天拿了HJ一手模板,舒服了。

HDU - 3555

ll n;
int digit[20];
ll dp[20][2];


ll dfs(int len, int sta, bool limit){
	if(len == 0) return 1;
	if(!limit && dp[len][sta]) return dp[len][sta];
	int up = limit ? digit[len] : 9;
	ll ret = 0;
	for(int i = 0; i <= up; i++){
		if(sta && i == 9) continue;
		ret += dfs(len - 1, i == 4, limit && i == up);
	}
	if(!limit) dp[len][sta] = ret;
	return ret;
}

void solve(ll num){
	int tot = 0;
	while(num){
		digit[++tot] = num % 10;
		num /= 10;
	}
	printf("%lld\n", n + 1 - dfs(tot, 0, 1));
}


int main()
{
	int T; scanf("%d", &T);
	while(T--){
		memset(dp, 0, sizeof dp);
		scanf("%lld", &n);
		solve(n);
	}
	return 0;
}

不写dfs的好像就不是很好写了

LL N;
LL S[100];
LL dp[100][3];
void init(){
	dp[0][0] = 1;
	for(int i = 1 ; i < 21 ; i++){
		dp[i][0] = dp[i-1][0] * 10 - dp[i-1][1];
		dp[i][1] = dp[i-1][0];
		dp[i][2] = dp[i-1][2] * 10 + dp[i-1][1];
	}
}


int main()
{
	init();
	int T;	scanf(" %d",&T);
	while(T--){
		scanf(" %lld",&N);
		int len = 0;
		while(N){
			S[++len] = N % 10;
			N /= 10;
		}
		S[len+1] = 0;
		LL ans = 0LL;
		bool flag = false;
		for(int i = len ; i >= 1 ; i--){
			ans += (dp[i-1][2] * S[i]);
			if(flag)	ans += dp[i-1][0] * S[i];
			if(!flag && S[i] > 4)	ans += dp[i-1][1];
			if(S[i+1] == 4 && S[i] == 9)	flag = true;
		}
		if(flag)	ans++;
		printf("%lld\n",ans);
	}
	return 0;
}

 

 

HDU - 3652

dp[i] [j] [k] , i 第i位 , j 是 mod , k是状态

0状态 , 前面没有 13 

1状态 没有 13 , 但最后一位是 1 

2状态 有13

int N;
int dp[30][13][3];
int a[30];

LL dfs(int len , int mod , int status , bool limit){
	if(!len) return (status==2) && (!mod);
	if(!limit && dp[len][mod][status])	return dp[len][mod][status];

	int res = 0;
	int ilen = (limit ? a[len] : 9);
	for(int i = 0 ; i <= ilen ; i++){
		int nxt_mod = (i + mod*10) % 13;
		int nxt_status = status;
		if(status == 0){
			if(i == 1)	nxt_status = 1;
		} else if(status == 1){
			if(i == 3)	nxt_status = 2;
			else if(i == 1)	nxt_status = 1;
			else nxt_status = 0;
		}
		res += dfs(len - 1 , nxt_mod , nxt_status , limit && i == ilen);
	}
	if(!limit)	dp[len][mod][status] = res;
	return res;
}

int main()
{
	while(~scanf(" %d",&N)){
		int len = 0;
		while(N){	a[++len] = N % 10; N /= 10;	}
		a[len+1] = 0;
		int ans = dfs(len , 0 , 0 , 1);
		printf("%d\n",ans);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值