Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 98517 | Accepted: 22352 |
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
Source
解题思路:搜索+剪枝。剪枝的几个思路如下:
1.由于原始的棍子是所有的长度都一样的,所以原始的棍子长度只可能是那些能够整除总长度的数。因此我们先计算出所有棍子的长度之和,然后对每一个能够整除该和的数值进行判断,看它是否可能是原始长度。
2.由于现在的棍子都是从原始棍子切割得到的,所以原始棍子一定不会短于现在的最长的棍子。
如果对于某个待判断的原始长度可能数值L,以上两条都满足,那么接下来就要通过回溯搜索的方法看是否能够将现有的这些棍子拼接成一个一个的长度为L的棍子。如何搜索呢?先在现有的棍子集合中找到一些拼出第一个长度为L的棍子,然后再拼第二个,然后第三个...这样依次下去,直到全部拼好,或者发现不能拼凑下去。
这里还有一些剪枝策略:
1.对于一个新的需要拼凑的目标,只需要随意找一个棍子作为它的第一个部件,如果该棍子作为它的第一个部件不成功,则其他的也必定不成功。
2.对于一个需要拼凑的目标,如果他缺少的部分正好是sticks[i](即L=now+sticks[i]),那么就直接将sticks[i]作为其最后一个部件,而不需要搜索其他可能了,即:如果sticks[i]放到这里不成功,那么换用比sticks[i]短的一个或多个棍子也必定不会成功。
由于第2条的存在,我们需要先对sticks[i]数组从大到小排序。
另外,在拼凑一个原始棍子的过程中,选择棍子的顺序是按数组从前向后选的,即:如果选择sticks[i]的时候已经选择了sticks[j],那么必须要j<i才能选择i,这样也是为了剪枝,或者说是避免重复搜索。
最后,还有一个小的trick:我们在凑一个个的L的时候,需要凑多少个呢?是sum/L个吗?不需要!只需要凑够(sum/L)-1个就行了,因为剩下的那些棍子就构成了最后的一个长度为L的棍子。不过这个好像也对剪枝帮助不大。
/*
* main.cpp
*
* Created on: 2012-7-20
* Author: wwf
*/
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool judge(vector<int> &sticks, vector<bool> &visited, int left, int L, int count, int sum, int index);
int main() {
while (true) {
int n;
cin >> n;
if (n == 0)
break;// end of input
vector<int> sticks(n);
int sum=0;
int _max=0;
for (int i = 0; i < n; i++) {// read lengths of the sticks got after cutting
cin >> sticks[i];
sum+=sticks[i];
_max=max(_max,sticks[i]);
}
sort(sticks.begin(),sticks.end(),greater<int>());
for(int L=_max;L<=sum;L++){
//cout<<"L = "<< L <<endl;
vector<bool> visited(sticks.size(),false);
if(sum%L==0&&judge(sticks,visited,L,L,0,sum,0)){
cout<<L <<endl;
break;
}
}
}
return 0;
}
bool judge(vector<int> &sticks, vector<bool> &visited, int left, int L, int count, int sum, int index) {
if (L*(count+1)==sum)//只需要凑够sum/L-1个就够了,剩余的棒子长度之和自然是L个了。
return true;//凑够了
for(int i=index;i<sticks.size();i++){
/*
* 有三种情况不需要遍历sticks[i](即不能再使用sticks[i]了):
* 1.sticks[i]已经被使用过了
* 2.sticks[i]添加之后会超过需要凑够的数L
* 3.该木棒之前的一根木棒与该木棒值相等,却没有被使用。这种情况主要是避免多个长度相同的木棒中任选一个所造成的冗余搜索。
*/
if(visited[i]||sticks[i]>left||(i>0&&sticks[i]==sticks[i-1]&&!visited[i-1]))continue;
/*
* 如果没有以上情况,则可以进行搜索考察
* 这里有一个特殊情况:如果sticks[i]加上之后刚好凑够一个L(即sticks[i]==left),那么就不用搜索j>i的情况了(对于任意的j>i,sticks[j]<=sticks[i])。这个很容易证明。
*/
if(sticks[i]==left){//刚好凑够,不需要再看其他选择了
visited[i]=true;
bool result=judge(sticks, visited, L, L, count+1, sum, 0);
visited[i]=false;
return result;
}else if(left==L){//从头开始,就随便拿一个就行了
visited[i]=true;
bool result=judge(sticks, visited, left-sticks[i], L, count, sum, i+1);
visited[i]=false;
return result;
}else{//没有凑够,普通情况
visited[i]=true;
bool result=judge(sticks, visited, left-sticks[i], L, count, sum, i+1);
visited[i]=false;
if(result)return true;
}
}
return false;
}