数位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])//如果已经有了数字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;
}

  

原文地址:https://www.cnblogs.com/qq936584671/p/9272970.html