标签:
如果以下错的地方,谢谢提出。
解题思路:这题的限制条件是不能出现4,和数字中不能包含62,那么就对这两个进行特判即可
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 12
int n, m;
int dp[N][N];
int data[N];
//zero_flag是前导零的标记,为0时表示有前导0
//border_flag时边界标记
//pos表示当前位
//pre_num表示前一个数
int dfs(int zero_flag, int border_flag, int pos, int pre_num) {
if (pos == -1)
return 1;
if (!border_flag && dp[pre_num][pos] != -1)
return dp[pre_num][pos];
int ans = 0;
int end = border_flag ? data[pos]: 9;
for (int i = 0; i <= end; i++) {
if(!(zero_flag || i))
ans += dfs(0, 0, pos - 1, i);
else if((pre_num == 6 && i == 2) || (i == 4))
continue;
else
ans += dfs(1, border_flag && (i == data[pos]), pos - 1, i);
}
if(!border_flag)
dp[pre_num][pos] = ans;
return ans;
}
int solve(int num) {
int cnt = 0;
while (num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(0, 1, cnt-1, 0);
}
void init() {
memset(dp, -1, sizeof(dp));
}
int main() {
init();
while (scanf("%d%d", &n, &m) != EOF && n + m) {
printf("%d\n", solve(m) - solve(n - 1));
}
return 0;
}
题目大意:求出范围內,数字中含有数字49的有多少个
解题思路:这题比上一题难了一点,上一题只要出现4或者发现连续的62就可以continue掉了,但是这题不一样,出现49后表示后面所有的数字都是符合要求的,也就是说,在pos,pre_num的情况下,会出现两种情况,符合要求的和不符合要求的,所以要再上一题的情况下再加一维,表示是否是符合条件的
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 70
ll n;
ll dp[N][12][2];
int data[N];
ll dfs(int zero_flag, int border_flag, int pre_num, int pos, int flag) {
//枚举完了,还要判断一下是否是符合要求的
if (pos == -1 )
return flag;
if (!border_flag && dp[pos][pre_num][flag] != -1) {
return dp[pos][pre_num][flag];
}
ll ans = 0;
int end = border_flag ? data[pos] : 9;
for (int i = 0; i <= end; i++) {
if(!(zero_flag || i))
ans += dfs(0, border_flag && (i == end), i, pos - 1, 0);
else if (pre_num == 4 && i == 9)
ans += dfs(1, border_flag && (i == end), i, pos - 1, 1);
else
ans += dfs(1, border_flag && (i == end), i, pos - 1, flag);
}
if (!border_flag)
dp[pos][pre_num][flag] = ans;
return ans;
}
ll solve(ll num) {
int cnt = 0;
while (num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(0, 1, 11, cnt - 1, 0);
}
void init() {
memset(dp, -1, sizeof(dp));
}
int main() {
init();
int test;
scanf("%d", &test);
while (test--) {
scanf("%lld", &n);
printf("%lld\n", solve(n));
}
return 0;
}
题目大意:奇数位的数要大于等于两边的数
解题思路:在枚举的时候特判一下就可以了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 12
int l, r;
int data[N];
int dp[N][N][2];
int dfs(int zero_flag, int border_flag, int pre_num, int pos, int even_flag) {
if (pos == -1)
return 1;
if (!border_flag && dp[pos][pre_num][even_flag] != -1)
return dp[pos][pre_num][even_flag];
int ans = 0;
int end = border_flag ? data[pos] : 9;
for (int i = 0; i <= end; i++) {
if (! (zero_flag || i)) {
ans += dfs(0, 0, 10, pos - 1, 1);
}
else if (!even_flag && i >= pre_num) {
ans += dfs(1, border_flag && (i == end), i, pos - 1, even_flag ^ 1);
}
else if(even_flag && i <= pre_num) {
ans += dfs(1, border_flag && (i == end), i, pos - 1, even_flag ^ 1);
}
}
if(!border_flag)
dp[pos][pre_num][even_flag] = ans;
return ans;
}
int solve(int num) {
int cnt = 0;
while(num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(0, 1, 10, cnt - 1, 1);
}
void init() {
memset(dp, -1, sizeof(dp));
}
int main() {
init();
int test;
scanf("%d", &test);
while (test--) {
scanf("%d%d", &l, &r);
printf("%d\n", solve(r) - solve(l - 1));
}
return 0;
}
中文题
解题思路:还是比较简单的,直接在枚举时特判就好了
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define ll long long
#define N 70
ll n, m;
int data[N];
ll dp[N][14];
ll dfs(int zero_flag, int border_flag, int pre_num, int pos) {
if (pos == -1)
return 1;
if (!border_flag && dp[pos][pre_num] != -1)
return dp[pos][pre_num];
ll ans = 0;
int end = border_flag ? data[pos] : 9;
for (int i = 0; i <= end; i++) {
if (!(zero_flag || i))
ans += dfs(0, 0, 13, pos - 1);
else if (abs(pre_num - i) >= 2)
ans += dfs(1, border_flag && (i == end), i, pos - 1);
}
if (!border_flag)
dp[pos][pre_num] = ans;
return ans;
}
ll solve(ll num) {
int cnt = 0;
while (num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(0, 1, 13, cnt -1);
}
void init() {
memset(dp, -1, sizeof(dp));
}
int main() {
init();
while (scanf("%lld%lld", &n, &m) != EOF) {
printf("%lld\n", solve(m) - solve(n - 1));
}
return 0;
}
以下的几题是需要思考下的
解题思路:预先处理一下F(A),然后将处理完后的FA作为参数传入
设dp[pos][num]为扫描到了第pos位,后面的数位按照要求计算后,满足小于等于num的有多少个
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 12
const int S = (1 << 9) * 10;
int data[N], pow2[N];
int A, B;
int dp[N][S];
int dfs(int border_flag, int pos, int num) {
if (pos == -1)
return num >= 0;
if (num < 0)
return 0;
if (!border_flag && dp[pos][num] != -1)
return dp[pos][num];
int end = border_flag ? data[pos] : 9;
int ans = 0;
for (int i = 0; i <= end; i++)
ans += dfs(border_flag && (i == end), pos - 1, num - pow2[pos] * i);
if (!border_flag)
dp[pos][num] = ans;
return ans;
}
int solve(int num) {
int cnt = 0;
while (num) {
data[cnt++] = num % 10;
num /= 10;
}
int cnt2 = 0;
int FA = 0;
while(A) {
FA += (A % 10) * pow2[cnt2++];
A /= 10;
}
return dfs(1, cnt - 1, FA);
}
void init() {
pow2[0] = 1;
for (int i = 1; i < N; i++)
pow2[i] = pow2[i - 1] << 1;
memset(dp, -1, sizeof(dp));
}
int main() {
init();
int test, cas = 1;
scanf("%d", &test);
while (test--) {
scanf("%d%d", &A, &B);
printf("Case #%d: %d\n", cas++, solve(B));
}
return 0;
}
题目大意:求一个范围內,符合最长上升子序列的长度为k的数字有多少个(1个数可以看成一个序列)
解题思路:看到最长上升子序列,应该想到的是nlog2n的算法,但是,如果实现呢
首先,数字上的数的范围在0-9,可以想到状态压缩,用状态压缩来代替栈
这里的话,多处了个状态,而且还有个限制k,所以可以将dp[pos][state][k]表示位当前枚举到第pos位,前面的位的最长上升子序列状态为state,还需要在state状态上添加数字,使得最长上升子序列的长度再加k的情况有多少种
这样就可以计算了,具体可以先参考上一题
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 30
#define S (1<<11)
#define K 12
long long l, r;
//dp[i][j][k]表示枚举到了第i位,状态为j,子序列长度还需要+k才能满足需求的有多少个
long long dp[N][S][K];
int k, data[N];
//zero_flag是前导零的标记,board_flag表示边界标记,state表示状态,len表示还需要几个, high是state下的最高位是哪一位
long long dfs(int zero_flag, int board_flag, int pos, int state, int len, int high) {
//如果枚举完了,判断一下长度是否符合
if (pos == -1)
return len == 0;
//如果长度超过k了
if (len < 0)
return 0;
if (!board_flag && dp[pos][state][len] != -1)
return dp[pos][state][len];
int end = board_flag ? data[pos] : 9;
long long ans = 0;
for (int i = 0; i <= end; i++) {
if(!(zero_flag || i))
ans += dfs(0, board_flag && (i == end), pos - 1, 0, k, -1);
else if (state & (1 << i)) //如果当前这个数状态里面已经有了
ans += dfs(1, board_flag && (i == end), pos - 1, state, len, high);
else {
if(i > high) //如果枚举得大于最高位,那么序列长度+1,最高位变成i
ans += dfs(1, board_flag && (i == end), pos - 1, state | (1 << (i)), len - 1, i);
else { //否则按nlogn的方法
int t = 0, cnt = 0;
for(int j = i + 1; j <= 9; j++) {
if(state & (1 << j)) {
if(!cnt)
t = j;
cnt++;
}
}
//如果最高位被改变了
if(cnt == 1)
ans += dfs(1, board_flag && (i == end), pos - 1, (state ^ (1 << t)) | (1 << i), len, i);
else
ans += dfs(1, board_flag && (i == end), pos - 1, (state ^ (1 << t)) | (1 << i), len, high);
}
}
}
if (!board_flag) {
dp[pos][state][len] = ans;
}
return ans;
}
long long solve(long long num) {
int cnt = 0;
while (num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(0, 1, cnt - 1, 0, k, -1);
}
void init() {
memset(dp, -1, sizeof(dp));
}
int main() {
int test;
int cas = 1;
scanf("%d", &test);
init();
while (test--) {
scanf("%lld%lld%d", &l, &r, &k);
printf("Case #%d: %lld\n", cas++, solve(r) - solve(l - 1));
}
return 0;
}
题目大意:平衡数表示的是,把一个数的某一位当成平衡点,然后将这个数当成天平,平衡点左右两边的数的计算结果相等,就算一个平衡数,问所给区间內有多少个平衡数
解题思路:平衡数的支点只有一个,因为如果支点左移或者右移的话,数是不可能平衡的。
由此我们可以暴力枚举以每一位当平衡点的情况
枚举数的时候,可以将平衡点左边的数当成正数,右边的数当成负数,如果最后累加的和为0的话,就表示该数平衡了
#include <cstdio>
#include <cstring>
#define N 20
#define S 2000
#define ll long long
ll dp[N][N][S];
ll x, y;
ll data[N];
ll dfs(int border_flag, int pos, int pivot, int sum) {
if(sum < 0)
return 0;
if (pos == -1) {
return sum == 0;
}
if(!border_flag && dp[pos][pivot][sum] != -1)
return dp[pos][pivot][sum];
ll ans = 0;
int end = border_flag ? data[pos] : 9;
for(int i = 0; i <= end; i++)
ans += dfs(border_flag && (i == end), pos - 1, pivot, sum + (pos - pivot) * i);
if(!border_flag)
dp[pos][pivot][sum] = ans;
return ans;
}
ll solve(ll num) {
if(num == -1)
return 0;
if(num == 0)
return 1;
int cnt = 0;
while(num) {
data[cnt++] = num % 10;
num /= 10;
}
ll ans = 0;
for(int i = 0; i < cnt; i++)
ans += dfs(1, cnt - 1, i, 0);
return ans - cnt + 1;
}
int main() {
int test;
scanf("%d", &test);
memset(dp, -1, sizeof(dp));
while (test--) {
scanf("%lld%lld", &x, &y);
printf("%lld\n", solve(y) - solve(x - 1));
}
return 0;
}
8.CodeForces - 55D Beautiful numbers
题目大意:问一个范围內有多少个数满足,该数能被每一位上的数整除
解题思路:如果k能被a和b整除,那么k也能被lcm(a,b)整除,lcm(2-9)的结果是2520,也就是说,如果能除尽2520的,那么必然也能够被2-9的数除尽,所以余数的保留可以用余2520表示
又因为下一个余数是上一个的余数* 10 + i,所以余2520可以变成余252,最后一位再特判一下就好了
设dp[pos][lcm][mod]为扫描到了第pos位了,前面的所有位的数的lcm的结果为lcm,余数位mod的情况下符合要求的平衡数有多少个
因为能被2520除尽的数不会超过50个,所以可以用一个数组进行映射
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 20
#define ll long long
ll dp[N][50][252];
ll l, r;
int data[N];
int Index[2521];
void init() {
memset(dp, -1, sizeof(dp));
int cnt = 0;
for (int i = 1; i <= 2520; i++)
if (2520 % i == 0)
Index[i] = cnt++;
}
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
ll dfs(int border_flag, int pos, int lcm, int sum) {
if(pos == -1)
return sum % lcm == 0;
if(!border_flag && dp[pos][Index[lcm]][sum] != -1)
return dp[pos][Index[lcm]][sum];
ll ans = 0;
int end = border_flag ? data[pos] : 9;
for(int i = 0; i <= end; i++) {
int t = lcm;
if(i)
t = lcm / gcd(lcm, i) * i;
int tt = sum * 10 + i;
if(pos)
tt %= 252;
ans += dfs(border_flag && (i == end), pos - 1, t, tt);
}
if(!border_flag)
dp[pos][Index[lcm]][sum] = ans;
return ans;
}
ll solve(ll num) {
int cnt = 0;
while (num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(1, cnt - 1, 1, 0);
}
int main() {
init();
int test;
scanf("%d", &test);
while (test--) {
scanf("%lld%lld", &l, &r);
printf("%lld\n", solve(r) - solve(l - 1));
}
return 0;
}
解题思路:这题比较难,要记录的状态比较多
首先了解一下(a + b) ^2 = a ^2 + b ^ 2 + 2 * a * b
设sum为后缀和,sum2为后缀平方和,num为满足要求的数的数量
当枚举到第pos位,枚举数为i时,就可以更新一下有pos位的数,且以i开头的,符合要求的有几个,后缀和,后缀平方和
以下边看代码边理解比较好理解
设tmp为dfs所得的结果,ans为要返回的结果
数量的话就num加一下就好了,关键是后缀和sum和后缀平方和sum2怎么更新
后缀和相对比较简单
tmp.sum += (pow(10,pos) * i * tmp.num + tmp.sum) 因为有tmp.num个后缀,每个后缀都要加上pow(10,pos) * i,然后再加上本身的后缀和,既是更新的后缀和了
根据上面的公式,后缀平方和
ans.sum2 += pow(10,pos) * pow(10,pos) * i * i *tmp.num(a ^ 2)
ans.sum2 += 2 * pow(10,pos) * i * tmp.sum(2 * a * b,因为有tmp.num个,而tmp.num个的后缀和刚好是tmp.sum)
ans.sum2 += tmp.sum2
既是更新的后缀平方和
记得取余
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define N 30
#define ll long long
const int mod = 1e9+7;
//sum为后缀和,sum2为后缀平方和,num为数量
struct Node {
ll num, sum, sum2;
Node() {
num = sum = sum2 = 0;
}
}dp[N][N][N];
int data[N];
ll pow10[N];
ll l, r;
Node dfs(int board_flag, int pos, int sum_f, int mod_f) {
Node ans, tmp;
if (pos == -1) {
ans.num = (sum_f && mod_f);
return ans;
}
if (!board_flag && dp[pos][sum_f][mod_f].num != -1)
return dp[pos][sum_f][mod_f];
int end = board_flag ? data[pos] : 9;
for (int i = 0; i <= end; i++) {
if (i == 7)
continue;
tmp = dfs(board_flag && (i == end), pos - 1, (sum_f + i) % 7, (mod_f * 10 + i) % 7);
ans.num = (ans.num + tmp.num ) % mod;
ans.sum = (ans.sum + (pow10[pos] * i % mod * tmp.num + tmp.sum) ) % mod;
ans.sum2 = (ans.sum2 + (pow10[pos] * pow10[pos] % mod * i * i % mod * tmp.num) + (2 * pow10[pos] * i % mod * tmp.sum) % mod + tmp.sum2) % mod;
}
if(!board_flag)
dp[pos][sum_f][mod_f] = ans;
return ans;
}
ll solve(ll num) {
int cnt = 0;
while (num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(1, cnt - 1, 0, 0).sum2;
}
void init() {
memset(dp, -1, sizeof(dp));
pow10[0] = 1;
for(int i = 1; i < 20; i++)
pow10[i] = (pow10[i - 1] * 10 ) % mod;
}
int main() {
init();
int test;
scanf("%d", &test);
while (test--) {
scanf("%lld%lld", &l, &r);
printf("%lld\n", (solve(r) - solve(l - 1) + mod) % mod);
}
return 0;
}
漏了一题。。。
HDU - 3652 B-number
这题的话就不写题解了,相信经过了上面这几道题,这题水题以不再话下了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 15
int n;
int data[N], dp[N][N][N][2];
int dfs(int zero_flag, int border_flag, int pre_num, int pos, int mod, int flag) {
if (pos == -1) {
if (flag && !mod)
return 1;
return 0;
}
if (!border_flag && dp[pos][mod][pre_num][flag] != -1)
return dp[pos][mod][pre_num][flag];
int end = border_flag ? data[pos] : 9;
int ans = 0;
for (int i = 0; i <= end; i++) {
if (pre_num == 1 && i == 3) {
ans += dfs(1, border_flag && (i == end), i, pos - 1, (mod * 10 + i) % 13, 1);
}
else
ans += dfs(1, border_flag && (i == end), i, pos - 1, (mod * 10 + i) % 13, flag);
}
if(!border_flag)
dp[pos][mod][pre_num][flag] = ans;
return ans;
}
int solve(int num) {
int cnt = 0;
memset(dp, -1, sizeof(dp));
while(num) {
data[cnt++] = num % 10;
num /= 10;
}
return dfs(0, 1, 0, cnt - 1, 0, 0);
}
int main() {
while(scanf("%d", &n) != EOF ) {
printf("%d\n", solve(n));
}
return 0;
}
希望以上对你有帮助!
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/l123012013048/article/details/47030803