hdu6468(记忆化搜索)
zyb的面试
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 695 Accepted Submission(s): 254
Problem Description
今天zyb参加一场面试,面试官听说zyb是ACMer之后立马抛出了一道算法题给zyb:
有一个序列,是1到n的一种排列,排列的顺序是字典序小的在前,那么第k个数字是什么?
例如n=15,k=7, 排列顺序为1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9;那么第7个数字就是15.
那么,如果你处在zyb的场景下,你能解决这个问题吗?
有一个序列,是1到n的一种排列,排列的顺序是字典序小的在前,那么第k个数字是什么?
例如n=15,k=7, 排列顺序为1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9;那么第7个数字就是15.
那么,如果你处在zyb的场景下,你能解决这个问题吗?
Input
T组样例(T<=100)
两个整数n和k(1<=n<=1e6,1<=k<=n),n和k代表的含义如上文
两个整数n和k(1<=n<=1e6,1<=k<=n),n和k代表的含义如上文
Output
输出1-n之中字典序第k小的数字
Sample Input
1
15 7
Sample Output
15
Source
思路:见代码
#include<cstdio> #include<algorithm> using namespace std; int dis[10];//把n分解 int cnt,len,ans,k,n; bool lg1; int ws[10];//判断每一位都取值时最终的值是否可能超过n int dp[10];//取到底的记忆化 int les[10];//不取到底的记忆化(当前值已经大于n并且还没到底,就返回了) int dfs(int pos,int sum,int po){//sum*po为当前值,ws[pos]为上界, if(sum*po<ws[pos]&&dp[pos]!=-1&&(cnt+dp[pos]<k)){//当找的范围还在k内 cnt+=dp[pos];//当当前值小于上界时,就记忆化每一位都取的情况 return dp[pos]; } else if(sum*po>ws[pos]&&les[pos]!=-1&&(cnt+les[pos]<k)){ cnt+=les[pos];//当当前值大于上界,就记忆化至少有一位不选的情况(当至少有一位没取时可以保证 return les[pos];//最后得到的值一定小于n } int sum2=cnt; int sum1=0; if(sum<=n){//当当前值满足条件时 sum1++; cnt++; if(cnt==k){ ans=sum; lg1=false; return 0; } } else return 0; if(pos==0) return sum1; for(int i=0;i<=9&&lg1;i++){//往下走 sum1+=dfs(pos-1,sum*10+i,po/10); } //printf("%d %d\n",sum*po,ws[pos]); if(sum*po<ws[pos])//当没有到上界 dp[pos]=sum1; else if(sum*po>ws[pos])//当到上界 les[pos]=cnt-sum2; return sum1; } int main(){ int t; scanf("%d",&t); while(t--){ cnt=0; scanf("%d%d",&n,&k); len=0; fill(dp,dp+8,-1); fill(les,les+8,-1); int m=n; int po=1; while(m){ po*=10; dis[++len]=m%10; m/=10; } int r=1; int summ=0; for(int i=1;i<len;i++){ summ+=dis[i]*r; ws[i]=n-summ; r*=10; } lg1=true; for(int i=1;i<=9&&lg1;i++){ dfs(len-1,i,po/10); } printf("%d\n",ans); } return 0; }