• 周三. 6月 29th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

UVa 12212 Password Remembering (数位dp)

admin

11月 28, 2021

题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=244&page=show_problem&problem=3364

(dp[i][j][0/1][0/1][0/1/2][0/1/2]) 表示当前是原数的第 (i) 位,翻转后是翻转数的第 (j) 位(因为有前导零,所以位置可能不变),原数卡不卡下界,原数卡不卡上界,前 (i) 位的原数翻转后小于/等于/大于后 (j) 位的上/下界

转移分类讨论即可,其中翻转后数字的大小关系,是从低位向高位维护的

注意原数前导零和最后连续的零翻转后的变化,小心处理即可,还要使用 (unsigned long long)

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
typedef unsigned long long ull;

const int maxn = 70;

int T;
ull A, B;
ull dp[maxn][maxn][2][2][3][3];
int a[maxn], b[maxn];
int ca, cb;

ull dfs(int pos, int rpos, int llx, int lls, int lrx, int lrs, int lead){ // llx 原数下界, lls 原数上界, lrx 翻转数下界, lrs 翻转数上界 
	if(pos == 0) {
		int fx = 0, fs = 0;
		if(rpos-1 >= ca && lrx >= 1) fx = 1;
		if(rpos-1 < cb || (rpos-1 == cb && lrs <= 1)) fs = 1;
		if(fx && fs) return 1;
		else return 0; 
	}
	if(!lead && dp[pos][rpos][llx][lls][lrx][lrs] != -1) return dp[pos][rpos][llx][lls][lrx][lrs];
	
	int lmx = llx ? a[pos] : 0;
	int lms = lls ? b[pos] : 9;
	
	ull res = 0;
	for(int i = lmx ; i <= lms ; ++i){ // 枚举原数第 i 个位置 
		int ns, nx;
		if(i > b[rpos]){ // 新的位置的数大于翻转数上界 
			ns = 2; 
		} else if(i == b[rpos]){ // 等于 
			ns = lrs;
		} else{ // 小于 
			ns = 0;
		}
			
		if(i < a[rpos]){ // 新的位置小于翻转数下界 
			nx = 0;
		} else if(i == a[rpos]){
			nx = lrx;
		} else{
			nx = 2;
		}
		
		if(lead && i == 0) { // 前导 0, 并且满足下界条件 
			res += dfs(pos-1, rpos, 1, 0, 1, 1, 1);
		}
		else res += dfs(pos-1, rpos+1, llx && (i == lmx), lls && (i == lms), nx, ns, 0);
	}
	
	if(!lead) dp[pos][rpos][llx][lls][lrx][lrs] = res;
	return res;
}

ull solve(){
	memset(a, 0, sizeof(a));
	memset(b, 0, sizeof(b));
	memset(dp, -1, sizeof(dp));
	ull tmp = A;
	ca = 0, cb = 0;
	while(tmp){
		a[++ca] = tmp % 10;
		tmp /= 10;
	}
	
	tmp = B;
	while(tmp){
		b[++cb] = tmp % 10;
		tmp /= 10;
	}
	
	return dfs(cb, 1, 1, 1, 1, 1, 1);
}

int main(){
	int T;
	scanf("%d", &T); 
	for(int kase = 1 ; kase <= T ; ++kase){
		scanf("%llu%llu", &A, &B);
		printf("Case %d: %llu
", kase, solve());
	}
	return 0;
}

发表评论

您的电子邮箱地址不会被公开。