(dfs)lintcode1739 · 最大为 N 的数字组合

题目

描述
我们有一组排序的数字 D,它是 {‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’} 的非空子集。(请注意,‘0’ 不包括在内。)

现在,我们用这些数字进行组合写数字,想用多少次就用多少次。例如 D = {‘1’,‘3’,‘5’},我们可以写出像 ‘13’, ‘551’, ‘1351315’ 这样的数字。

返回可以用 D 中的数字写出的小于或等于 N 的正整数的数目。

规模

D 是按排序顺序的数字 ‘1’-‘9’ 的子集。
1 <= N <= 10^9

样例

输入:D = [“1”,“3”,“5”,“7”], N = 100
输出:20
解释:
可写出的 20 个数字是:
1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.

分析

该题用于学习dfs的方法,但是根据数据规模有可能超时,先计算一下,N最大为1e9,数组中元素的个数最多为9个,就相当于给定九个位置每个位置都可以选数组中的数字,情况一共有多少种,每个位置都是九种选择,一共九个位置,那就是九的九次方,大概是四亿多,不出意外的情况下应该是超时了,但是这里主要为了学习dfs的方法我们暂时先用dfs来写这个算法。

下面是dfs需要的参数

	//给定数组,数组中的元素的个数,组合出数字的值
		//N为数字的上限,ans为最终答案(组合出小于N的数字的个数) 
        void dfs(vector<string> D,int n,long long now_number,int N,int &ans)

有了参数之后我们要确定dfs中的两大项
一,是dfs的出口条件
二,是dfs要做的事情
一般来说我们要先确定二

一 确定dfs要做的事情

不变的量:

D(给定的字符串数组)
n(字符串数组的长度)
N(组合出来的数最大不能超过的值)
ans(可以组合出来的种类):因为这里ans我们传递的是引用,所以直接在函数中自加即可

需要变得量

只有一个now_number(表示现在组合出来的数字),now_number要如何变化呢,比如现在只有两个元素1和2要怎么一步一步的组合呢,首先选出1,递归之后跟2组合变成了12,所以这里的now_number每次都要乘十再加上下一个需要组合的数字

下面是dfs的递归式

		for(int i=0;i<n;i++)
		{
			int tmp=D[i][0]-'0';
			dfs(D,n,now_number*10+tmp,N,ans);
		} 

二 确定dfs的出口条件

随着dfs的深入我们组合出来的数字也会越来越大,题目中的限制条件是N,那出口条件就是now_number>N

下面是出口条件

		//出口
		if(now_number>N)	
			return ;
			ans++

ans在每次dfs中组合出来的数字都加上1,这个时候我们要思考细节,根据我们写的程序进行模拟的递推,首先我们传入进dfs的now_number是0,然后就会发现0也被算成了一种答案,所以我们在函数中返回ans的时候要ans-1

程序完整代码

class Solution {
public:

	void dfs(vector<string> D,int n,long long now_number,int N,int &ans)
	{
		//出口
		if(now_number>N)	
			return ;
			
		ans++;
		//现在做的事情
		for(int i=0;i<n;i++)
		{
			int tmp=D[i][0]-'0';
			dfs(D,n,now_number*10+tmp,N,ans);
		} 
	}

    int atMostNGivenDigitSet(vector<string> &D, int N) {
        int n=D.size();
        
        int ans=0;
        //给定数组,数组中的元素的个数,组合出数字的值
		//N为数字的上限,ans为最终答案(组合出小于N的数字的个数) 
        dfs(D,n,0,N,ans);
        
        return ans-1;
    }
};

后来在lintcode官网上测试了数据最大的情况,果然超时了,结果显示大概需要循环四亿多次。这道题其实可以运用数学的方法,比如N=1e9,就相当于给了就9个位置,给了i个元素,那么每个位置的选择情况就有i种,所以,最终的ans就是i的九次方,例如N=1e8+10那么前1e8的情况我们直接运用公式就可以算出来,后面的情况我们在用暴力的方法去计算,就避免了超时的情况

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

White boy&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值