Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 133058 | Accepted: 31236 |
Description
Input
Output
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
Sample Output
6 5
思路还可以,就是剪枝。。。。剪枝。。。。剪枝,咋想的啊。。。想破脑子也就想出来两个剪枝的地方,一直超时,大神一剪剪5个地方,一个
if (len == 0) break;,跪了。。。
思路:先从大到小排序,因为大的组合方式相对较少,先考虑(剪枝1)
InLen一定大于或者等于maxn,(否则你要组成的一条棍子还没有原来的长,不可能),并且要小于等于sum的一半,否则最后组成比如
17, 15这样的情况不用dfs,直接pass (剪枝2)
sum必须要能够整除InLen(剪枝3)
差不多了没啥事咱开始dfs吧
用一个sample来记录跳过当前层不能用的连续的棒子(剪枝4)
当一个InLen长的棒子合成之后开始合成新的InLen长的棒子时,若新的第一块碎片递归下去不能成功(也就是下面所有的的递归都返回false,因为包含这个碎片的所有的情况在返回的时候都考察过了,说明以上一根合成的结果为起点走下去不行,上一根棍子合成的有问题),则直接返回,否则继续走的话也不会成功,因为到最后,这第一块碎片迟早要面对的,在后面面对的时候跟第一次面对的时候没什么区别,第一次面对的时候不行,后面再面对的时候还是不行,因为第一次面对的时候已经把所有包含这第一块碎片的情况都考察进去了(剪枝5)
最后一种剪枝感觉不太好理解
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int s[100];
bool vis[100];
bool cmp(int a, int b) {
return a > b;
}
int N;
bool dfs(int len, int InLen, int num, int pos) {
if (num == N) return true;
for (int i = pos, sample = -1; i < N; i++) {
if (!vis[i] && sample != s[i]) {
vis[i] = true;
if (len + s[i] < InLen) {
if (dfs(len + s[i], InLen, num + 1, i)) {
return true;
} else {
sample = s[i];
}
} else if (len + s[i] == InLen) {
if (dfs(0, InLen, num + 1, 0)) {
return true;
} else {
sample = s[i];
}
}
vis[i] = false;
if (len == 0)
break;
}
}
return false;
}
int main()
{
int sum, maxn;
bool flag;
while (~scanf("%d", &N) && N) {
memset(vis, false, sizeof(vis));
sum = 0;
for (int i = 0; i < N; i++) {
scanf("%d", &s[i]);
sum += s[i];
}
sort(s, s + N, cmp); //降序排序
maxn = s[0]; //取最大的
flag = false;
//maxn <= InLen <= sum / 2
for (int InLen = maxn; InLen + InLen <= sum; InLen++) {
if (!(sum % InLen) && dfs(0, InLen, 0, 0)) {
flag = true;
printf("%d\n", InLen);
break;
}
}
if (!flag) {
printf("%d\n", sum);
}
}
return 0;
}