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;
}

如有错误接受批评和指正,谢谢您的阅读

 

posted @ 2023-08-02 10:08  552Hz  阅读(14)  评论(0)    收藏  举报