URAL 2052 Physical Education(数位dp)



题意:给出一个自然数数列,按照每个数的所有数位之和作为第一关键字,每个数的大小作为第二关键字升序排序,位置不变的数的个数是多少。

思路:首先可以证明,对于数位和为i的所有数,最多只可能有一个位置不变,这个可以直观的猜想一下,因为如果有一个数字位置不变,那么对于排序后的序列,这个数后面的所有数的增长速度都大于自然数序列的增长速度,所以不可能再有第二个。

假设我们当前求出了数位和为i的区间为[l, r],令query(a, b)表示1到a这段区间内数位和为b的数的个数,用su[i-1]表示数位和为1到i-1的数的个数。

那么对于区间[l, l+query(l-1, i) -1],一定不存在位置不变的数,因为这些数都小于l,

那么我们就把区间缩小为了[su[i-1]+query(l-1, i)+1, su[i-1]+query(r, i)],因为至多只存在一个位置不变的数,所以这个区间肯定可以一直缩小下去,这个递归来做就可以。

递归的出口是l==r且su[i-1]+query(l-1, i)+1==su[i-1]+query(r,i),这时返回1,否则返回0。也就是说,这时l位置的这个数就是位置不变的一个数。

现在问题转化为了求query,对于这个问题,我们用一个辅助的数组dp[][][]帮助我们计算,dp[i][j][k]表示一个第i位为j数字和为k的数有多少,并且第i位是这个数的最高位。

对于query,每次如果当前位的数字比给定的n的这一位的数字小,那么后面可以随便选,这样一来就可以解决query的问题,具体见代码。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#define eps 1e-6
#define LL long long
using namespace std;

//const int maxn = 100 + 5;
//const int INF = 0x3f3f3f3f;
int n, dp[20][20][100], su[100];
void init() {
	for(int i = 0; i <= 9; i++) dp[1][i][i] = 1;
	for(int i = 2; i <= 10; i++) {
		for(int j = 0; j <= 9; j++) {
			for(int t = 0; t <= 81; t++) {
				for(int k = 0; k <= 9; k++)
					if(t>=j) dp[i][j][t] += dp[i-1][k][t-j];
			}
		}
	}
}
int query(int r, int digit_sum) {
	int digit[20], len=0;
	int tmp = r;
	while(tmp) {
		digit[++len] = tmp % 10;
		tmp /= 10;
	}
	int cur_sum = 0;
	int ans = 0;
	for(int i = len; i >= 1; i--) {
		for(int j = 0; j < digit[i]; j++) {
			if(digit_sum-j-cur_sum >= 0)
				ans += dp[i][j][digit_sum-cur_sum];
		}
		cur_sum += digit[i];
	}
	if(cur_sum == digit_sum) ans++;
	return ans;
}
void init_su() {
	for(int i = 1; i <= 81; i++)
		su[i] = su[i-1] + query(n, i);
}
int solve(int l, int r, int digit_sum) {
	int left_border = su[digit_sum-1] + query(l-1, digit_sum)+1;
	int right_border = su[digit_sum-1] + query(r, digit_sum);
	if(left_border > right_border) return 0;
	if(left_border==right_border) {
		if(su[digit_sum-1]+query(left_border-1,digit_sum)+1 == su[digit_sum-1] + query(right_border, digit_sum)) return 1;
		return 0;
	}
	return solve(left_border, right_border, digit_sum);
}
int main() {
	//freopen("input.txt", "r", stdin);
	init();
	while(cin >> n) {
		init_su();
		//for(int i = 1; i <= 9; i++) cout << i << ": " << query(n, i) << endl;
		int ans = 0;
		for(int i = 1; i <= 81; i++) ans += solve(su[i-1]+1, su[i], i);
		//for(int i = 1; i <= 10; i++) cout << i << ": " << solve(su[i-1]+1, su[i], i) << endl;
		cout << ans << endl;
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-04 21:29:11

URAL 2052 Physical Education(数位dp)的相关文章

URAL 2052 Physical Education

题目链接:https://vjudge.net/contest/254142#problem/G 参考题解:https://blog.csdn.net/zearot/article/details/47984379 1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define LL __int128 5 #define ull unsigned long long 6 #define mst

URAL 1586. Threeprime Numbers 数位dp

题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1586 题解:dp[i][j][k]表示长度为i,最高位为j,次高位为k的合法方案数,转移方程为当j*100+k*10+l为质数时dp[i][j][k]+=dp[i-1][k][l]; #include<bits/stdc++.h> #include<set> #include<iostream> #include<string> #include&

Ural 1057 Amount of Degrees(数位DP)

题目链接:点击打开链接 题目大意:求给定区间[X,Y]中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和.例如,设X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:17 = 2^4+2^0, 18 = 2^4+2^1, 20 = 2^4+2^2. 1 ≤ X ≤ Y ≤ 2^31?1,1 ≤ K ≤ 20,  2 ≤ B ≤ 10. 思路:数位DP的思想, 因为本题满足区间减法, 所以我们只需要求出一个不大于n的满足要求的数的个数,那么先预处理出一个B进制数列

数位dp小记

转载自:http://blog.csdn.net/guognib/article/details/25472879 参考: http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html kuangbin :http://www.cnblogs.com/kuangbin/category/476047.html http://blog.csdn.net/cmonkey_cfj/article/details/7798809 http:/

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>

【HDU 3652】 B-number (数位DP)

B-number Problem Description A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task