【数位DP】ZOJ2599Graduated Lexicographical Ordering
2016-02-23 23:23
381 查看
传送门
Time Limit:10S Memory Limit:32768KB
Description
我们定义一种独特的给数排序的方法:
对于两个数,数码和较小的排在前面。因此120120排在44前面,44排在42294229前面。对于两个数码和一样的数,字典序小的排在前面。因此555555排在7878前面,2020排在200200前面。
现在给你NN个数1 N1~N,希望你对他们进行排序,然后求:kk排在第几个?第kk个是谁?
多组测试数据。
Input
若干行。每一行两个数N,kN,k含义如上。k<=N<=1018k<=N<=10^{18}。以00 00作为数据结尾。
Output
每一行两个数即两问的答案。
Sample Input
2020 1010
00 00
Sample Output
22 1414
一道很恶心的数位DP 555~
先考虑第一问。可以通过DP求出数位之和比kk小的数,对于数位之和相同的数,再计算字典序比kk小的数(通过枚举前缀来计算)。
对于第二问,已经知道了对于每一个数位和的个数就可以确定第kk小的数的数位之和。然后再枚举前缀就可以确定这个数了。
事实上这些都是很好想的,就是代码不太好写。
Time Limit:10S Memory Limit:32768KB
Description
我们定义一种独特的给数排序的方法:
对于两个数,数码和较小的排在前面。因此120120排在44前面,44排在42294229前面。对于两个数码和一样的数,字典序小的排在前面。因此555555排在7878前面,2020排在200200前面。
现在给你NN个数1 N1~N,希望你对他们进行排序,然后求:kk排在第几个?第kk个是谁?
多组测试数据。
Input
若干行。每一行两个数N,kN,k含义如上。k<=N<=1018k<=N<=10^{18}。以00 00作为数据结尾。
Output
每一行两个数即两问的答案。
Sample Input
2020 1010
00 00
Sample Output
22 1414
一道很恶心的数位DP 555~
先考虑第一问。可以通过DP求出数位之和比kk小的数,对于数位之和相同的数,再计算字典序比kk小的数(通过枚举前缀来计算)。
对于第二问,已经知道了对于每一个数位和的个数就可以确定第kk小的数的数位之和。然后再枚举前缀就可以确定这个数了。
事实上这些都是很好想的,就是代码不太好写。
#include <iostream> #include <cstdio> #define LL long long int using namespace std; LL n, k, dp[20][205]; //数位之和是sum且有pos位的数的个数 LL dfs(int pos,int sum) { if(sum>9*pos||sum<0||pos<0)return 0; if(!pos&&!sum)return 1; if(dp[pos][sum])return dp[pos][sum]; LL &ans=dp[pos][sum]; for(int i=0;i<10&&i<=sum;++i) ans+=dfs(pos-1,sum-i); return ans; } //计算1~n中数位之和是sum的个数 LL dfs2(LL n,int sum) { int tmp[20], len=0; while(n) { tmp[len++]=n%10; n/=10; } LL ans=0; for(int i=len-1;~i;--i) for(int j=0;j<tmp[i];++j) ans+=dfs(i,sum--);//从高位到低位枚举每一位的数字,同时数位之和减少 ans+=dfs(0,sum);//如果n的数位之和也是sum,就在这里+1 return ans; } //求n的数位之和 int getsum(LL n) { int ans=0; while(n)ans+=n%10, n/=10; return ans; } //求前缀为pre、数位之和为sum且在1~n之间的数的个数 LL ask_pre(LL n,LL pre,int sum) { LL ans=0; int w[20], w2[20], len=0, len2=0; while(n){w[len++]=n%10;n/=10;} while(pre){w2[len2++]=pre%10;sum-=w2[len2-1];pre/=10;} for(int i=0;i+i<len;++i)swap(w[i],w[len-i-1]); for(int i=0;i+i<len2;++i)swap(w2[i],w2[len2-i-1]); for(int i=0;i<len2;++i) if(w[i]!=w2[i]) { if(w[i]<w2[i])--len; for(int j=len-len2;~j;--j) ans+=dfs(j,sum); return ans; } //说明前缀pre是n的前缀,那么就统计后面的位上能产生的个数 LL tmp=0; for(int i=len2;i<len;++i)tmp=tmp*10+w[i]; ans+=dfs2(tmp,sum); for(int i=len-len2-1;~i;--i)ans+=dfs(i,sum); return ans; } //求字典序比k小的、数位之和为sum且1~n之间的数的个数 LL query(LL n,LL k,int sum) { int w[20], len=0; LL ans=0, pre=1; while(k) { w[len++]=k%10; k/=10; } for(int i=len-1, b=1;~i;--i) { for(int j=b;j<w[i];++j)ans+=ask_pre(n,pre++,sum); b=0; pre*=10; } for(int i=0;i<len;++i) if(w[i]==0)++ans; else break; return ans; } //求第一问的答案 LL ans1(LL n,LL k) { int sum=getsum(k); LL ans=1; for(int i=1;i<sum;++i)ans+=dfs2(n,i); return ans+query(n,k,sum); } //求第二问的答案 LL ans2(LL n,LL k) { int sum=1; LL tmp; while((tmp=dfs2(n,sum))<k) k-=tmp, ++sum; LL pre=1;int pre_sum=1; while(1) { while((tmp=ask_pre(n,pre,sum))<k) { k-=tmp; ++pre, ++pre_sum; } if(pre_sum==sum)break; pre*=10; } while(--k)pre*=10; return pre; } int main() { while(~scanf("%lld%lld",&n,&k)&&n) printf("%lld %lld\n",ans1(n,k),ans2(n,k)); return 0; }
相关文章推荐
- Linux /proc/buddyinfo理解
- 教你如何破解MagicZoom插件!
- 原生态JAVAEE酒店管理系统系列三
- liunx下的jdk的安装-以及全局环境变量的配置
- [转载]二叉树(BST,AVT,RBT)
- 51nod 1405 树的距离之和 (两次dfs,树形dp)
- MySQL存储过程 IN,OUT,INOUT
- Android版本和API Level
- POJ 3469_Dual Core CPU
- POJ 3469_Dual Core CPU
- POJ 3469_Dual Core CPU
- Lucene笔记---全文检索引擎工具包
- LeetCode OJ ----Two Sum
- GoldenGate架构下oracle与oracle数据迁移
- 别了,产品经理的能力模型
- 工作小结_2016年2月23日23:16:59
- 我的大数据之路(二):从zookeeper说起
- Mysql优化建议
- PowerDNS简单教程(4):优化篇
- 包装类的自动装箱与拆箱