数位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('\n')
#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])//如果已经有了数字i,则消除高位,操作数不增加
ans+=dfs(pos-1,(m&c[i]),kk,limit&&i==up);
else//如果没有数字i,则消除高位,操作数增加
ans+=dfs(pos-1,(m&c[i])|b[i],kk+(i!=0),limit&&i==up);//如果i是0,则不计入操作数
}
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;
}

浙公网安备 33010602011771号