ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)
题意
定义两个数的比较方法,各位数字之和大的数大,如果数字和相等则按字典序比较两个数的大小。输入n,k,求:1.数字k的排名;2.排名为k的数。
思路
算是一类经典的统计问题的拓展吧~
先来看第一问。求数字K的排名,变相得看就是求[1,N]中小于K的数的个数,数位DP统计下即可(记忆化搜索方式,dfs()过程):pos表示处理的位置;dig_sum表示当前枚举的数位和;隐藏的全局比较对象k_sum是K的数位和;start表示枚举数位开始的位置,即第一个非零位,便于按位DP时比较字典序;flag表示字典序是否小于K,-1表示小于,0表示未决定,1表示大于,比较过程在按位DP过程中即完成(详见next_flag计算);limit表示当前数位的数字是否达上限。
然后来看第二问。如果是像此题这样和数大小不成线性关系的排名规则,尝试正着求排名可能会比较麻烦。所以一般处理这种排序问题的方法是用某种方法枚举符合规则的数,然后判断这个数是不是排第K(求一个数的排名用第一问的方法)。一般枚举数可以二分,blablabla……当然这道题二分不行,因为字典序和数位和规则都不具备单调性。而朴素枚举显然不行,在这道题中,大家普遍的方法是枚举前缀~具体一点儿就是:枚举第一位是几,再枚举第二位是几,直到确定这个数字。枚举每一位时都是从0~9(除了第一位不能是0),这样就保证了枚举的顺序符合字典序递增的顺序。而我们也可以通过枚举数位和来算出第K个数的数位和(见代码)。总结一下第二问的思路就是:先算出第K个数的数位和k_dig_num,然后枚举前缀,根据当前前缀、数位和为k_dig_num下的数的个数,由于字典序,所以前面的数一定小于第K个数,这样下去知道当前枚举到的数是第K个数。
代码
[cpp]
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, end) for (int i = begin; i <= end; i ++)
using namespace std;
typedef unsigned long long LL;
typedef vector <int> VI;
int k_sum, k_dig_sum;
VI num, k_num;
LL dp[25][200][25][5], fsum[25][200];
LL res1, res2;
int sum;
LL dfs(int pos, int dig_sum, int start, int flag, bool limit){
if (pos == -1){
if (dig_sum == 0) return 0;
if (dig_sum > k_sum) return 0;
else if (dig_sum == k_sum){
if (!flag)
if ((int)k_num.size()-1-(start-pos) >= 0)
return 1;
else
return 0;
else{
return (flag==-1)?1:0;
}
}
else if (dig_sum < k_sum) return 1;
}
if (!limit && ~dp[pos][dig_sum][start][flag]) return dp[pos][dig_sum][start][flag];
int end = limit?num[pos]:9; LL res = 0;
REP(i, 0, end){
bool st = (start == pos && i == 0);
int next_flag;
if (flag != 0) next_flag = flag;
else{
if (st) next_flag = 0;
else {
if ((int)k_num.size()-1-(start-pos) < 0) next_flag = 1;
else{
if (i < k_num[(int)k_num.size()-1-(start-pos)]) next_flag = -1;
else if (i == k_num[(int)k_num.size()-1-(start-pos)]) next_flag = 0;
else next_flag = 1;
}
}
}
res += dfs(pos-1, dig_sum+i, st?start-1:start, next_flag, limit&&(i==end));
}
return limit?res:dp[pos][dig_sum][start][flag]=res;
}
LL find_dig_sum(int pos, int dig_sum, bool limit){
if (pos == -1) return dig_sum == sum;
if (!limit && ~fsum[pos][dig_sum]) return fsum[pos][dig_sum];
int end = limit?num[pos]:9;
LL res = 0;
REP(i, 0, end){
res += find_dig_sum(pos-1, dig_sum+i, limit&&(i==end));
}
return limit?res:fsum[pos][dig_sum]=res;
}
inline void cal(LL x){
num.clear();
while(x){
num.push_back(x%10);
x /= 10;
}
}
inline void cal_k(LL k){
k_num.clear();
k_sum = 0;
while(k){
k_sum += k%10;
k_num.push_back(k%10);
k /= 10;
}
}
LL solve_pos(LL n, LL k){
if (k == 0) return 0;
cal_k(k);
cal(n);
MEM(dp, -1);
return dfs(num.size()-1, 0, num.size()-1, 0, 1) + 1;
}
LL solve_rank(LL n, LL k){
LL sum_num = 0;
LL dig_sum_rank = k;
for (sum = 1; sum <= 180; sum ++){
MEM(fsum, -1);
sum_num += find_dig_sum(num.size()-1, 0, 1);
if (sum_num >= k){
k_dig_sum = sum;
if (sum > 1){
dig_sum_rank -= sum_num - find_dig_sum(num.size()-1, 0, 1);
}
break;
}
}
LL testnum = 0, sumranks = 0;
while(solve_pos(n, testnum) != k){
testnum *= 10;
LL ranks = 0;
REP(i, 0, 9){
ranks = 0;
if (testnum == 0 && i == 0) continue;
LL begin = testnum+i;
LL offset = 1;
while(begin<=n){
cal(min(n, begin+offset-1));
ranks += find_dig_sum(num.size()-1, 0, 1);
cal(begin-1);
ranks -= find_dig_sum(num.size()-1, 0, 1);
begin *= 10;
offset *= 10;
}
if (sumranks + ranks >= dig_sum_rank){
testnum = testnum + i;
break;
}
sumranks += ranks;
}
}
return testnum;
}
LL n, k;
int main(){
while(scanf("%lld %lld", &n, &k), n+k){
res1 = res2 = -1;
res1 = solve_pos(n, k);
res2 = solve_rank(n, k);
printf("%lld %lld\n", res1, res2);
}
return 0;
}
[/cpp]
ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)的更多相关文章
- ZOJ 2599 Graduated Lexicographical Ordering (数位DP)
首先要吐两行槽:看到集训队论文上有这道题,由于数位DP一律写成记忆化搜索形式的强迫症,就没去看论文上的几个函数是什么……:结果被这道题虐的脑细胞死光……,最后是用随机数据对拍AC程序然后发现BUG改掉 ...
- ZOJ2599:Graduated Lexicographical Ordering(很经典的数位DP)
Consider integer numbers from 1 to n. Let us call the sum of digits of an integer number its weight. ...
- ZOJ 3494 (AC自动机+高精度数位DP)
题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3494 题目大意:给定一些被禁止的BCD码.问指定范围内不含有 ...
- zoj 3962 Seven Segment Display 数位dp
非常好的一个题,可以比赛时想到的状态太奇葩,不方便转移,就一直没能AC. 思路:dp(i, j)表示已经考虑了前i位,前i位的和为j的贡献.如果当前的选择一直是最大的选择,那么就必须从0~下一位的最大 ...
- ZOJ 3494 BCD Code (数位DP,AC自动机)
题意: 将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的.给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的 ...
- ZOJ 3494 BCD Code(AC自动机+数位DP)
BCD Code Time Limit: 5 Seconds Memory Limit: 65536 KB Binary-coded decimal (BCD) is an encoding ...
- ZOJ 3962 Seven Segment Display(数位DP)题解
题意:给一个16进制8位数,给定每个数字的贡献,问你贡献和. 思路:数位DP,想了很久用什么表示状态,看题解说用和就行,其他的都算是比较正常的数位DP. 代码: #include<iostrea ...
- ZOJ 3962 Seven Segment Display(数位DP)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3962 题目大意: 有t组数据. 给你一个n,和8位的十六进制数s ...
- ZOJ 3962 E.Seven Segment Display / The 14th Zhejiang Provincial Collegiate Programming Contest Sponsored by TuSimple E.数位dp
Seven Segment Display Time Limit: 1 Second Memory Limit: 65536 KB A seven segment display, or s ...
随机推荐
- CentOS忘记用户名或者密码解决办法
方法一:如果用户名和密码都忘记了,可以用以下这个方法找回:(和第二种大步骤差不多,需注意3,4步) 1. 在出现grub画面时,用上下键选中你平时启动linux的那一项,然后按e键 2. 再次用上下键 ...
- vertical-align和line-height的深入应用
vertical-align和line-height的深入应用 本文的重点是了解vertical-align和line-height的使用 涉及到的名词:基线,底端,行内框,行框,行间距,替换元素及非 ...
- The Air Jordan 4 Oreo Remastered would be re-released in 2015
May be the Jordan 4 Oreo probably the most anticipated pair among the remastered Jordans for 2015? W ...
- vmware12 安装linux centos6
内核选 2.6 中文安装选 基本系统 -> 基本, 兼 ...
- C++匿名名字空间
转自:http://blog.csdn.net/eric_arrow/article/details/8978905 名字空间(namespace),是C++提供的一个解决符合名字冲突的特性.标准规定 ...
- HDU - 4871 Shortest-path tree (最短路径树+ 树分治)
题意:给你一张带权无向图,先求出这张图从点1出发的最短路树,再求在树上经过k个节点最长的路径值,以及个数. 分析:首先求最短路树,跑一遍最短路之后dfs一遍即可建出最短路树. 第二个问题,树分治解决. ...
- CentOS 6.5上安装python2.7、pip以及Python命令行补全和yum冲突解决
目前CentOS6.5上自带的python版本为2.6,升级到python2.7会碰到很多问题.本文将介绍如何安装python2.7.pip以及python命令行补全. 一.如何安装python2.7 ...
- SqlHelper简单实现(通过Expression和反射)9.Sql表述对象SqlSession
此类是整个SqlHelper的另一个核心,基本思想就是通过EntityHelper,ObjectHelper和ExpressionHelper获取拼接Select语句的全部元素,拼接出完整Select ...
- Pyspark访问Hbase
作者:Syn良子 出处:http://www.cnblogs.com/cssdongl/p/7347167.html 转载请注明出处 记录自己最近抽空折腾虚拟机环境时用spark2.0的pyspark ...
- 同时执行2条不同sql
select * from a: select *from b; 用分号结束.