HDU 1258

这是我没有经过讨论写出来的,思维比较狭隘些吧,因为有人比我快很多AC了,所以希望看过我的代码的高手们给我提些建议和意见~谢谢咯~如果有比较简单高效的方法也请留言~~欢迎欢迎~~微笑

/*
HDU 1258 SUM IT UP
Sample Input
4 6 4 3 2 2 1 1
5 3 2 1 1
400 12 50 50 50 50 50 50 25 25 25 25 25 25
0 0
Sample Output
Sums of 4:
4
3+1
2+2
2+1+1
Sums of 5:
NONE
Sums of 400:
50+50+50+50+50+50+25+25+25+25
50+50+50+50+50+25+25+25+25+25+25
大致就是将给的总和total用给的数列里的数用和式计算出来将所有的和式都列出来,然后列式的要求是数字从大到小
总的思路用了DFS,把大问题分解成了小问题。就是把一个t分成一个m+t然后递归求t的和式,打印出来,再找下一个m。。
Q1:出了如4 3 2 2 1 1的数列,当用到最后一个1(p=5)的时候,明明只用了一个1,
但是由于visit[1]在取到num[4]的时候就输出一下,之后又见到num[4]的时候就又输一遍
1所以就出问题了。
然后解决方法是:判断当前的num[i]和num[i+1]是否相等,如果相等的话,就continue,直到走到了相同数的最后一个,
然后找visit[num[i]]打印。
Q2:如果有数列数相同的话,可能会坐标不同数字一样而重新搜过,并且重复打印。但是如果在递归的时候就直接判断
是否数字一样,然后不进行这个数的搜索的话,又会有4=2+2,搜不到这种情况,所以不能这么简单地把数字相等的都不进行递归。
解决方法:又设了一个数组remember[],进行记忆,打印前对比一下visit[]和remember[],如果两个完全一样的话,就不再输出。
*/
#include<stdio.h>
#include<string.h>
int N;
int num[101];
int visit[1000]={0};
int remember[1000]={0};
int DFS(int t,int n,int p,int m)
{
	int re=0;
	int sign=0;
	int i,j;
	if(t==m)
	{
		visit[m]++;
		for(i=0;i<=p;i++)
		{
			if(visit[num[i]]==remember[num[i]])
			{
				sign=1;
			}else
			{
				sign=0;
				break;
			}
		}
		if(sign)
		{
			visit[m]--;
			return 0;
		}
		for(i=0;i<=p;i++)
		{
			if(num[i+1]==num[i]&&(i+1)<=p)
				continue;
			for(j=0;j<visit[num[i]];j++)
			{
				printf("%d",num[i]);
				if(!(i==p&&j==visit[num[i]]-1))
					printf("+");
			}
		}
	
		printf("\n");
		for(i=0;i<=p;i++)
		{
			remember[num[i]]=visit[num[i]];
		}
		visit[m]--;
		return 1;
	}
	t=t-m;
	if(p+1>=n||t<0)
	{
		return 0;
	}
	visit[m]++;
	for(i=p;i<n;i++)
	{
		//if(num[i+1]==num[i]&&(i+1)<n)
			//continue;
		re+=DFS(t,n,i+1,num[i+1]);
	}
	visit[m]--;
	return re;
}
int main()
{
	int t,p,re=0;
	int i,j;
	while(1)
	{
		scanf("%d %d",&t,&N);
		if(N==0)
			return 1;
		memset(num,0,100);
		for(i=0;i<N;i++)
			scanf("%d",&num[i]);
		p=0;
		printf("Sums of %d:\n",t);
		memset(visit,0,1000);
		memset(remember,0,1000);
		for(i=0;i<N;i++)
		{
			if(num[i]==num[i-1]&&(i-1)>=0)
				continue;
			re+=DFS(t,N,i,num[i]);
		}
		if(!re)
			printf("NONE\n");
		re=0;
	}
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值