来博客园的第一天
题目是木棒
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。
然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。
请你设计一个程序,帮助乔治计算木棒的可能最小长度。
每一节木棍的长度都用大于零的整数表示。
输入格式
输入包含多组数据,每组数据包括两行。
第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。
第二行是截断以后,所得到的各节木棍的长度。
在最后一组数据之后,是一个零。
输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
数据范围
数据保证每一节木棍的长度均不大于50。
输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5
先声明一下:木棍是原来的木棒,木棒是切割后的木棍,(这样表述不容易混乱)。
思路是:
1、先考虑没有剪枝的搜索,枚举所有木棍的长度。如果当前的木棒不能被拼成一个木棍就返回上一层,如果所有的木棒都不可以那么就枚举下一个木棍的长度;
2、一定是有解的(最差也是所有木棒拼成一个木棍);
3、考虑剪枝(1):我们很容易想到木棍的长度一定是大于等于现在最长的木棒,并且小于所有木棒总和的长度;
4、考虑剪枝(2):我们可以从大到小枚举组成每个木棒。可以简单证明:因为短的木棒具有更好的灵活性,可利用价值高,如果先考虑短木棒的话后面的长木棒拼接木棍时就变得很困难,但是如果先考虑长木棒的话或许这个木棍长度就是最小的;
5、考虑剪枝(3):如果当前木棒和上一个木棒长度一样并且上一个木棒并没有成功,那么当前木棒也不会成功,可以直接跳过(就不用向下搜索了)。证明(反证法):如果当前木棒可以成功那么和上一个木棒交换就一定可以成功,因为上一个不可以成功那么这个就矛盾了;
6、考虑剪枝(4):如果当前木棍长度不能被总的木棒和除尽,那么一定不行,因为要求每个木棒都会用到,而且所有木棍都是一样长的;
7、考虑剪枝(5):如果枚举某个木棍组成时第一个木棒就失败了就不用枚举以后的木棒了。反证法:如果这个木棒失败了,但是这个木棍长度是可以满足所有木棒的,那么在枚举后面的木棒时一定会用到第一个木棒,由于第一个木棒失败了,所以以后肯定会有木棍会组合失败,那么就矛盾了;
8、考虑剪枝(6):如果枚举某个木棍的最后一个木棒时失败了,那么就不用枚举以后的木棒了。反正法:如果这个木棒失败了,可以通过交换的方式把这个木棒放到后面的某个木棍组合中去,那么就一定会有某个组合会失败,所以就不用枚举后面的木棒了;
code:
#include <iostream> #include <algorithm> #include <cstring> using namespace std; const int N = 64 + 10; //题意:有一堆等长的木棒切割他们并且每一个切割后的木棒长度都小于等于50个单位 //现在想要复原这些木棍,但是忘记了最初木棍的个数,和他们的长度. //现在要求出这些木棍的最小可能原始的长度 //切割后的数量最多是64个 int n; int len[N];//len用来保存输入的长度 int visit[N]; bool dfs1(int idx, int min_len, int sum) {//idx表示当前从第几个位置开始枚举木棒,min_len表示当前所枚举的木棍长度,sum表示当前木棒已经组成的长度 visit[idx] = 1; if (len[idx] + sum == min_len){ for(int i=0;i<n;++i){ if(!visit[i]){ if(!dfs1(i,min_len,0)){ visit[i]=0; return false; } } } return true; } if (len[idx] + sum < min_len) { for (int i = idx+1; i < n; ++i) { if (!visit[i]&&min_len-len[idx]-sum>=len[i]) { if (!visit[i - 1] && len[i] == len[i - 1])continue;//如果当前木棍和上一个木棍一样,并且上一个木棍没有成功就跳过 if (dfs1(i, min_len, sum + len[idx])) { return true; } visit[i] = 0; if(sum+len[idx]+len[i]==min_len){ visit[idx]=0; return false;//如果当前木棍失败了就不用枚举后面的木棍了 } } } return false; } } bool cmp(int a, int b) { if (a > b)return true; else return false; } int main() { ios::sync_with_stdio(false); while (cin >> n && n != 0) { int sum_len = 0; for (int i = 0; i < n; ++i) { cin >> len[i]; sum_len += len[i]; } sort(len, len + n, cmp);//从大到小排序 int max_len=len[0]; int i; for (i = max_len; i <= sum_len/2; ++i) { //cout << "******" << endl; if (sum_len%i != 0)continue; memset(visit, 0, sizeof(visit)); if (dfs1(0,i,0)) { cout << i << endl; break; } } if(i>sum_len/2)cout<<sum_len<<endl; } }