题意:
给出t(总和)和n,接下来给出n个数,用加号连接输出n个数字中所有满足和为t的式子。
且输出的式子中数字按降序排列,式子的先后按照字典序的方式比较,大的先输出(即先
比较第一个数的大小,如果相同则比较第二个数的大小,依次类推。。。)
算法:
1、由于式子中数字要按降序排列,故先把n个数按降序排列,这样也能保证式子之间输出
的先后关系。
2、由于最多只有12个数,dfs把每个数取与不取暴利一遍。
3、避免输出重复,用mark二维数组把之前的组合都记录,输出之前check一下是否重复。
<br/>
开始走入思维误区,觉得重复的一定会是相邻的数字,因为排了序,所以一定是相邻的状态,
所以只用了一维的mark数组,每次mark记录的是前一个满足和为t的组合,输出前只判断当前
和之前是否相同,相同则重复不输出。
其实也确实是相邻的数字(排序后)可能造成重复,但不是在递归搜索过程中只有相邻的两个
状态可能造成重复。
如果相邻且相等的数字在前面,则中间搜索状态将它们隔开,它们就不是相邻状态,用一维
MARK数组判重就行不通了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>
#include<vector>
using namespace std;
int a[20],t,n,flag,c;
bool vis[20];
int cnt[110];
vector<int> mark[1000];
bool cmp(int x,int y)
{
return x>y;
}
void out()
{
int f = 0;
for(int i=0;i<n;i++)
{
if(vis[i])
{
if(!f)
{
printf("%d",a[i]);
f = 1;
}
else printf("+%d",a[i]);
}
}
printf("\n");
}
bool check()
{
int num = 0;
for(int i=0;i<c;i++)
{
int s = 0;
for(int j=0;j<n;j++)
{
if(cnt[a[j]]!=mark[i][a[j]])
{
s = 1;
continue;
}
}
if(s) num++;
}
if(num == c) return true;
else return false;
}
void dfs(int id,int sum)
{
if(id>n || sum>t)
return;
if(sum == t && check())
{
flag = 1;
out();
for(int i=0;i<100;i++)
mark[c].push_back(cnt[i]);
c++;
return ;
}
int x = a[id];
vis[id] = true;
cnt[x]++;
dfs(id+1,sum+x);
vis[id] = false;
cnt[x]--;
dfs(id+1,sum);
}
int main()
{
while(scanf("%d%d",&t,&n)!=EOF)
{
if(t==0 && n==0)
break;
printf("Sums of %d:\n",t);
flag = 0;
c = 0;
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n,cmp);
int i;
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<110;i++)
mark[i].clear();
for(i=0;a[i]>t && i<n;i++);
if(i==n)
{
printf("NONE\n");
continue;
}
dfs(i,0);
if(!flag)
printf("NONE\n");
}
return 0;
}
/*
434 12 94 94 93 74 66 60 49 38 35 34 27 2
Sums of 434:
94+94+93+66+60+27
94+94+93+66+49+38
94+94+74+60+49+34+27+2
94+94+66+60+49+35+34+2
94+93+74+66+38+35+34
94+93+74+60+49+35+27+2
94+93+66+60+49+38+34
*/