acdream 1064

題目鏈接:http://115.28.76.232/problem?pid=1064

題目大意:給定一個區間,找出在這個區間內,數位裏有3或者有8但不能同時同時有3或8的數的個數

題目類型:數位dp

此題有兩個版本的寫法,時間複雜度一樣,但是代碼長度差很多

第一種是用dfs寫的,代碼如下:

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

int num[15];
int dp[15][3];


int dfs(int pos,int flag,int s)//pos表示考慮當前位,s表示之前的狀態,flag表示當前位是否可以任意
{
	 if(pos == -1)			// 邊界狀態
	 	return s==1||s==2;	
	 if(flag && dp[pos][s] != -1) // 剪枝
	 	return dp[pos][s];
	 int x = flag ? 9 : num[pos];
	 int ans=0;
	 for(int i=0;i<=x;i++)		//遞歸找接下來的位
	 {
	 	int k=s;
	 	if(i==3)
	 		k=1|s;
	 	if(i==8)
	 		k=2|s;
	 	if(k<=2)			// 剪枝
	 		ans+=dfs(pos-1,flag||i<x,k);	 //dp[pos][s]表示當前考慮pos位,之前的狀態爲s,接下的(pos+1)個位的組合滿足條件的個數
	 }
	 if(flag)
	 	dp[pos][s]=ans;
	 return ans;
}

int cal(int x)
{
	int k=0;
	while(x)
	{
		num[k++]=x%10;
		x/=10;
	}
	return dfs(k-1,0,0);
}


int main()
{
	int t;
	int l,r;
	scanf("%d",&t);
	memset(dp,-1,sizeof(dp));
	while(t--)
	{
		
		scanf("%d%d",&l,&r);
		printf("%d\n",cal(r)-cal(l-1));
	}
	return 0;
}

第二種寫法是用循環寫的,代碼如下:

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

long long dp[15][3];//分成三種情況,dp[i][0]表示都沒有,dp[i][1]表示只有3,dp[i][2]表示只有8
int num[15];

int cal(int x)
{
    int l=1;
    while(x)//分離數字存入num數組
    {
        num[l++]=x%10;
        x/=10;
    }
    int flag3=0;
    int flag8=0;//標記前位3和8出現否
    long long ans=0;
    for(int i=l-1;i>0;i--)
    {
        if(!flag3 && !flag8)    //3和8都沒出現過的時候
        {
            if(num[i]<=3)
            {
                ans+=num[i]*(dp[i-1][1]+dp[i-1][2]);//此情況只需考慮3和8單獨存在的情況
                if(num[i]==3)
                    flag3=1;
            }
            else
            {
                if(num[i]>3&&num[i]<=8)
                {
                    ans+=(num[i]-1)*(dp[i-1][1]+dp[i-1][2]);//補其他數字的情況
                    ans+=dp[i-1][1]+dp[i-1][0];
                    if(num[i]==8)
                        flag8=1;
                }
                else
                {
                    ans+=(num[i]-2)*(dp[i-1][1]+dp[i-1][2]);//補3或8的情況
                    ans+=dp[i-1][1]+dp[i-1][2]+dp[i-1][0]*2;
                }
            }
        }
        else
        {
            if(flag3&&!flag8)
            {
                if(num[i]<=8)
                {    
                    ans+=num[i]*(dp[i-1][1]+dp[i-1][0]);
                    if(num[i]==8)
                        flag8=1;
                }
                else
                {
                    ans+=(num[i]-1)*(dp[i-1][1]+dp[i-1][0]);
                }

            }
            else
            {
                if(!flag3&&flag8)   //8已存在,不要考慮3的情況
                {
                    if(num[i]<=3)
                    {
                        ans+=num[i]*(dp[i-1][2]+dp[i-1][0]);//少了補3的情況
                        if(num[i]==3)
                            flag3=1;
                    }
                    else
                    {
                        ans+=(num[i]-1)*(dp[i-1][2]+dp[i-1][0]);
                    }
                }
            }
        }

    }
    return ans;
}

int main()
{
    dp[0][0]=1;
    dp[0][1]=dp[0][2]=0;
    for(int i=1;i<15;i++)
    {
        dp[i][0] = 8* dp[i-1][0];               //在沒有3和8的基礎上,再補其他的8個數
        dp[i][1] = dp[i-1][1] * 9 + dp[i-1][0];//在只有3的基礎上補除8以外的數加上沒有3和8的基礎上補3
        dp[i][2] = dp[i-1][2] * 9 + dp[i-1][0];//在只有8的基礎上補除3以外的數加上沒有3和8的基礎上補8
    }
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",cal(r+1)-cal(l));
    }
}



發佈了19 篇原創文章 · 獲贊 7 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章