数位dp 51nod1623
Posted qq936584671
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位dp 51nod1623相关的知识,希望对你有一定的参考价值。
题目:www.51nod.com/onlineJudge/questionCode.html#!problemId=1623
题意描述看起来有点怪怪的,描述有一定错误,不过看了示例操作就知道要干什么了。
知道是数位dp,但是是之前没有做过的类型,而且题目的规则要仔细分析。
1. 对于一连串先递增再递减(先递减再递增)的数字,操作数为其中不同数字的个数。
如:12392 出现了1,2,3,9 操作数为4,所以要有一个状态数统计出现的数字,那么使用2进制统计即可。
结论:每出现一个状态数中没有出现的数字,将其加入状态数,并使操作数+1。
2. 对于多个情况 1复合的数字,很难直观的看出规律,先列几个例子:
13542 操作数为5 135423 操作数为6 1354234 操作数为7
13542343 操作数为7 135423434 操作数为8
这是一个很难总结的结论,看了别人的博客才总结出来,感觉一般情况下太难想到了。
结论:每新增加一个数字,就将状态数中比其大的数字除去。
结合之前得出的结论,就是这道题的解题思路了。
用之前的例子演示一下:
1 状态数1 操作数1
13 状态数13 操作数2
135 状态数135 操作数3
1354 状态数134 操作数4
13542 状态数12 操作数5
135423 状态数123 操作数6
1354234 状态数1234 操作数7
13542343 状态数123 操作数7
135423434 状态数1234 操作数8
由此得到了无关递增递减的一般化规则
在操作上,用或(|)操作添加状态,用与(&)操作消除比其大的数
实际写起来操作很简单,是一道锻炼思维的好题
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<stack> #include<map> #include<vector> #include<queue> #include<set> #include<iomanip> #include<cctype> #include<stack> using namespace std; const int MAXN=6e5+5; const int INF=1<<30; const long long mod=1e9+7; const double eps=1e-8; #define ll long long #define edl putchar(‘ ‘) #define sscc ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define FOR(i,a,b) for(int i=a;i<=b;i++) #define ROF(i,a,b) for(int i=a;i>=b;i--) #define FORLL(i,a,b) for(ll i=a;i<=b;i++) #define ROFLL(i,a,b) for(ll i=a;i>=b;i--) #define mst(a) memset(a,0,sizeof(a)) #define mstn(a,n) memset(a,n,sizeof(a)) #define zero(x)(((x)>0?(x):-(x))<eps) ll dp[20][1050][20],l,r,k; int a[20],b[15],c[15]; void init() { b[0]=0; b[1]=1; c[0]=0; FOR(i,2,10) b[i]=b[i-1]*2; FOR(i,1,9) c[i]=b[i+1]-1; FOR(i,0,18) FOR(j,0,c[9]) FOR(k,0,18) dp[i][j][k]=-1; } ll dfs(int pos,int m,int kk,int limit) { //cout<<pos<<" "<<m<<" "<<kk<<" "<<limit<<endl; if(pos==0) { return kk==k; } if(!limit&&dp[pos][m][kk]!=-1) { return dp[pos][m][kk]; } int up=limit?a[pos]:9; ll ans=0; FOR(i,0,up) { if(m&b[i]) ans+=dfs(pos-1,(m&c[i]),kk,limit&&i==up); else ans+=dfs(pos-1,(m&c[i])|b[i],kk+(i!=0),limit&&i==up); } if(!limit) dp[pos][m][kk]=ans; return ans; } ll solve(ll x) { int pos=0; while(x) { a[++pos]=x%10; x/=10; } a[pos+1]=0; return dfs(pos,0,0,1); } int main() { init(); cin>>l>>r>>k; cout<<solve(r)-solve(l-1)<<endl; }
以上是关于数位dp 51nod1623的主要内容,如果未能解决你的问题,请参考以下文章