CF Round #739 (Div 3) F, Nearest Beautiful Number题解
萌新的第一篇题解。
题意:找到大于n时的不同数位不超过k位时的最小值。
这是一道经典的数位dp的题目,但是相比于之前我做过利用数位dp求满足条件的数,这题是用数位dp来解决满足条件的最大值,跟普通的数位dp一样我们用记忆化搜索实现。x表示当前我们搜到的位数,larger表示之前是否有数字大于d_x位的数字,如果大于,则0开始搜,否则能从d[x]到9搜,cnt表示当前位置时已经使用的数位的个数,num表示当前数字。
larger保证了数字是大于n的,vis记录了当前使用的数位,如果小于等于k位则继续搜向下一层,这看似就是一个普通的搜爆,但是真的是这样吗?
时间复杂度分析:
正如一位朋友跟我说的数位dp的本质:“搜轮廓”。当我们需要回溯的情况,仅仅有当前添加位置所有数字都会使得其大于k。设我们在第x位的时候用了y位数位,那当我们数位大于d的时候我们能够即使退出当层的搜索。因为最多只有10个数位,我们的前x位的数位始终是趋向少的,并且因为我们是按照轮廓搜索的,所以第一个满足条件的值就是我们需要的值。最坏情况下的时间复杂度应为O(|S|*10)。
附上代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; int vis[11]; vector<int> d; int n,k; bool dfs(int x,bool larger,int cnt,int num){ if(x==d.size()){ cout<<num<<endl; return true; } else{ for(int i=larger?0:d[x];i<=9;i++) { vis[i]+=1; int ncnt=cnt; if(vis[i]==1) ncnt+=1; if(ncnt<=k&&dfs(x+1,larger|(i>d[x]),ncnt,num*10+i)){ return true; } vis[i]-=1; } return false; } } void solve(){ cin>>n>>k; for(int i=0;i<=9;i++){ vis[i]=0; } d.clear(); while(n!=0){ d.push_back(n%10); n/=10; } reverse(d.begin(),d.end()); dfs(0,0,0,0); } int main() { std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int _; cin>>_; while(_--){ solve(); } return 0; }
如有错误接受批评和指正,谢谢您的阅读

浙公网安备 33010602011771号