ACdream - 1726 A Math game(递归,避免重复top之前的不入栈 或 定义 top 光标)

Recently, Losanto find an interesting Math game. The rule is simple: Tell you a number  H, and you can choose some numbers from a set {a[1],a[2],......,a[n]}.If the sum of the number you choose is  H, then you win. Losanto just want to know whether he can win the game.
Input
There are several cases. 
In each case, there are two numbers in the first line  n (the size of the set) and  H. The second line has n numbers {a[1],a[2],......,a[n]}.  0<n<=40, 0<=H<10^9, 0<=a[i]<10^9,All the numbers are integers.
Output
If Losanto could win the game, output "Yes" in a line. Else output "No" in a line.
Sample Input
10 87
2 3 4 5 7 9 10 11 12 13
10 38
2 3 4 5 7 9 10 11 12 13
Sample Output
No
Yes


题意:输入 n.h,下面一行有n个数,从这n个数中任选几个数,看着几个数能不能和等于 h,若能就输出Yes  否则就输出No

这是我比赛时的思路,但是没有那个sum【】数组减值,但是我是用 tt[x-1]<=a[i]和book数组标记 ,这样避免重复的(像1,2,3,或3,2,1,或2,1,3等6种排列只能出现一种),这样时间复杂度太高了,但当时那个代码是个 wa,不也不知道哪出错了,(不要再想那个代码了,没意义,下面有更好的)但是那个代码要改进的地方太多了,就像为了避免重复,就定义一个top,让 top 之前的不能再次入栈,这样避免从重复(比上面那个好(时间复杂度低)),我又加了sum[] 数组减值,提交不知道为啥是 0 ms ,可能是后台数据太水了

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 50
long long book[Max];
long long a[Max],n,h,f,s[Max];


void dfs(long long top,long long x ,long long sum) //top 之前的不会再入栈,所以根本就不用book数组标记 
{													// x-1 为已经加了几个数了,sum为这几个数的和; 
	if(sum==h||f)
    {
         f = 1;
         return ;
    }
    if(x>n)
    {
        f = 2;
        return ;
    }
    for(long long i = top; i<=n; i++)
    {
         if(sum+(s[n]-s[i-1])<h)  // 这个减值挺好,以后一定要思考减值; 
        	return ;
         if(sum+a[i]>h)  
            continue;
         dfs(i+1,x+1,sum+a[i]);
         if(f) return ;
    }
}
int main()
{
    while(~scanf("%lld%lld",&n,&h))
    {
        for(long long i=1; i<=n; i++)
        {
        	scanf("%lld",&a[i]);
        	if(i==1) s[i] = a[i];
        	else s[i] = s[i-1] + a[i];
        }
        f = 0;
        memset(book,0,sizeof(book));
        dfs(1,1,0);
        if(f==1) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}


上面这些代码都是根据全排列的代码改编的,全排列就是看看这个数有没有被标记过,若没有被标记过,就把这个数存入数组中,若被标记过,就让 i++,看看下个数是否被标记 。 那么每一次调用函数,压入栈中,若假设这n个数都没有被标记过,那么这一栈,就要有n次递归,每个压入栈中的都要有n次递归的话,你想想这是多大的复杂度。所以呢,一般是不用全排列改编的代码的,上面代码已经改编的很好,时间复杂度降低了很多了,但是,你想想这用不用这样在每一个栈中判断一些数是不是符合情况,不需要让数在一个栈中枚举,只需,定义一个光标,让光标从开始移动,当光标移动到这个数时,这个数无非就两种情况,选或者不选,所以每一栈中就只会递归调用两次,(上一个代码时间短的原因我觉着就是,随说每一栈中都会调用递归多次,但压入栈中的数量不定,开始可能多,随着每一栈中的top增加,压入栈中的数量会越来越少,而下面这个代码压入栈中的数量,都是当光标移动到最后时,才返回)所以代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 50

long long sum[Max],a[Max],n,h,f;

void dfs(long long top,long long ans)
{
	if(ans==h)
	{
		f=1;
		return ;
	}
	if(top>n||ans>h||ans+(sum[n]-sum[top-1])<h) //减值 
		return ;
	dfs(top+1,ans);
	if(f) return ;
	dfs(top+1,ans+a[top]);
	if(f) return ;
}
int main()
{
	long long i,j;
	while(~scanf("%lld%lld",&n,&h))
	{
		for(i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			if(i==1) sum[i] = a[i];
			else sum[i] = sum[i-1] + a[i];
		}
		f = 0;
		dfs(1,0);
		if(f) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
} 

以后遇到这样的题先想想能不能定义光标top,让光标移动,让每一栈中的调用函数(递归)变少,这道题就是选不选的问题;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值