[51Nod1623] 完美消除

link

$solution:$

首先我们可以发现一个结论,对于一个数 $x$ ,它的最低修改次数为它每位与前去中是否都比此位上的数大,有则答案 $-1$ 。因为若有小数则没有办法将其答案贡献变低。

这个东西可以直接单调栈维护一个递增序列。

所以这样 $dp$ 状态也很显然了,设 $f_{i,j,sta}$ 表示当前到 $i$ 位,答案为 $j$ ,$sta$ 表示当前栈中 $0-9$ 是否在栈中。

简单数位转移即可。

时间复杂度 $O(10\times 18^2 \times 2^{10})$ 。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=21;
int dig[11],f[MAXN][MAXN][1<<10];
int l,r,k,cnt;
void debug(int sta){
    int Dig[10];
    Dig[0]=0;
    while(sta){
        Dig[++Dig[0]]=sta%2;
        sta/=2;
    }
    for(int i=Dig[0];i>=1;i--) printf("%d ",Dig[i]);printf("\n");
    return ;
}
int dfs(int ps,int K,int sta,int done){
    if(!done&&f[ps][K][sta]!=-1) return f[ps][K][sta]; 
    if(ps==0){
        if(K==k) return 1;
        return 0;
    }
    int end,Ans=0;
    if(done) end=dig[ps];
    else end=9;
    for(int i=0;i<=end;i++){
        int S=sta;
        for(int j=i+1;j<=9;j++) if(S&(1<<j)) S-=(1<<j);
        if(S&(1<<i)) Ans+=dfs(ps-1,K,S,done&(i==dig[ps]));
        else Ans+=dfs(ps-1,K+1,S+(1<<i),done&(i==dig[ps]));
    }
    if(done==0) f[ps][K][sta]=Ans;
    return Ans;
}
int solve(int x){
    dig[0]=0;
    while(x){
        dig[++dig[0]]=x%10;
        x/=10;
    }
    return dfs(dig[0],0,1,1);
}
signed main(){
    freopen("digit.in","r",stdin);
    freopen("digit.out","w",stdout);
    memset(f,-1,sizeof(f));
    l=read(),r=read(),k=read();
    printf("%lld\n",solve(r)-solve(l-1));return 0;
}
View Code

 

posted @ 2019-07-28 20:07  siruiyang_sry  阅读(205)  评论(0编辑  收藏  举报