zoukankan      html  css  js  c++  java
  • 数位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;
    }
    

      

  • 相关阅读:
    2021,6,10 xjzx 模拟考试
    平衡树(二)——Treap
    AtCoder Beginner Contest 204 A-E简要题解
    POJ 2311 Cutting Game 题解
    Codeforces 990G GCD Counting 题解
    NOI2021 SDPTT D2T1 我已经完全理解了 DFS 序线段树 题解
    第三届山东省青少年创意编程与智能设计大赛总结
    Luogu P6042 「ACOI2020」学园祭 题解
    联合省选2021 游记
    Codeforces 1498E Two Houses 题解 —— 如何用结论吊打标算
  • 原文地址:https://www.cnblogs.com/qq936584671/p/9272970.html
Copyright © 2011-2022 走看看