题目大意:
给你一个数字序列里面的数字都是不同的,让你找出不同组合的加法式,但是等式中的加数和结果都必须在给定的数字序列中(已知数字序列中的数字不会重复)。
解题思路:
题目要求输出的时候首先按式子的长度排序输出,之后如果式子长度相同,那么按照第一个加数在序列中的顺序进行排序输出。
先贴个我自己写的代码吧,是一堆垃圾,琢磨了好久还是没做出来,但是看了题解自后,觉得思想是完全相同的
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int max1,n;
int count1;
int num[50];
bool vis[50];
int result[50];
int t;
int sign=0;
int flag;
void dfs(int sum,int pos,int count)
{
int i,j;
if(sum<max1&&count==count1+1)
return ;
if(sum>max1||count>count1+1)
return ;
if(sum==max1&&count==count1+1)
{
for(j=0;j<t;j++)
if(j==0)
printf("%d",result[j]);
else
printf("+%d",result[j]);
sign=1;
flag=1;
return ;
}
for(i=pos+1;i<=n;i++)
{
if(vis[i]==0&&num[i]<=max1)
{
sum+=num[i];
vis[i]=1;
result[t++]=num[i];
count++;
dfs(sum,i,count);
result[--t]=0;
++t;
vis[t--]=0;
count--;
sum-=num[i];
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("f:\\in.txt","r",stdin);
#endif
int i,j,k;
int T;
int d;
scanf("%d",&T);
while(T--)
{
t=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&num[i]);
sort(num+1,num+1+n);
flag=0;
for(i=3,max1=num[i];i<=n;i++)
{
max1=num[i];
for(count1=1,t=0;count1<=n-2;count1++)
{
memset(result,0,sizeof(result));
memset(vis,0,sizeof(vis));
d=num[1];
t=0;
result[t++]=num[1];
vis[1]=1;
sign=0;
dfs(d,0,1);
if(sign==1)
printf("=%d\n",max1);
}
if(sign==0&&flag==0)
printf("Can't find any equations.\n");
}
printf("\n");
}
return 0;
}
这份代码一开始是想按照和值来搜索,以长度作为限制条件,比如,先搜等于3的,那么从长度为2的开始搜索。每次加一个数字判断是否超出我当前要搜索的和值,并且是否已经超出了所限制的长度条件。
过了样例之后,一直还是WA,然后也感觉自己写的很烂,输出等地方有很多漏洞,就没有再改
后来看了解题报告之后,发现有两点不同,第一个是处理输出,我想先存再输出,但是很复杂,还有一个就是我长度的控制总是出现问题,而且按照每个和值来搜索的话,每一个和值都要从长度2搜索到长度n-1,这样也会浪费很多时间。
后来想到,既然已经排序,那么我们就从第一个开始搜索,每次检查相加的和值是否小于这个数列中最大的那个数值,如果小于,那么证明有可能组合为一个等式,每次加上一个数之后,相应的长度都要剪1,假设我们现在搜索长度为2的等式,那么加了两个数的时候长度就变为0,可以开始输出工作。得到和值之后,例如 sum=1+2,那么我们要在2的后面去寻找是否有等于他们和值的数字,只要找到后面的数字有一个大于他们的和值为止。
如果找到那我们就输出,没有找到就退出。假设我们一开始找了1+2,但是发现数列中没有和3相等的数字,那么就把2换掉,去搜索后面的数字。这样的搜索方式,每次都是依次以序列中的数字为起点向后搜索,知道最后一个点为起点,那么就退出。而等式的长度又是一个大循环,将这些操作都包含在里面。搜索顺序的确定,自然也就方便的输出的顺序。
再说输出:
如果我现在找到了1+2,那么从2的位置开始,一直搜索到n-1,如果2位置后面的数字小于1+2的和值,那么它也许是答案,但是我们要进一步判断,如果2的位置后面的数字大于1+2的和值了,那么这个数字的后面以及它本身都不再是答案,可以退出。输出的时候从第一个已经被标记的数字开始输出,每次输出之后,和值sum都要减掉刚输出的数字,直到sum和最后一个要输出的加数相等时候,一起把最后一个加数和和值一起输出。
代码如下:
//题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1204
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int num[50];
bool vis[50];
int result[50];
int n;
int flag=0;
void dfs(int start,int len,int sum)
{
int i,j,q;
if(len==0)
{
for(i=start;i<n&&sum>=num[i];++i)
{
if(sum==num[i])
{
flag=1;
for(j=0;j<=i;j++)
{
if(vis[j])
{
if(sum==num[j])
printf("%d=%d\n",num[j],num[i]);
else
printf("%d+",num[j]);
sum-=num[j];
}
//sum-=num[j];
}
}
}
}
else
{
for(q=start;q<n;q++)
{
if(sum+num[q]<=num[n-1])
{
sum+=num[q];
vis[q]=1;
--len;
dfs(q+1,len,sum);
vis[q]=0;
sum-=num[q];
++len;
}
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("f:\\in.txt","r",stdin);
#endif
int i,j,k;
int T;
int d;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&num[i]);
sort(num,num+n);
memset(vis,0,sizeof(vis));
flag=0;
for(j=2;j<=n-1;j++)
dfs(0,j,0);
if(!flag)
printf("Can't find any equations.\n\n");
else
printf("\n");
}
return 0;
}