这是我没有经过讨论写出来的,思维比较狭隘些吧,因为有人比我快很多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;
}
}