Park on the first day to blog, write a search algorithm bar

The first day to blog Park

Entitled stick

George brought sticks of equal length, they are cut off at random, the length of each section such that the stick is not more than 50 units of length.

Then he wants to return to these sticks is cut before the cut-off state, but can not remember how many there are initial length of the stick, and the stick initially.

Please design a program that might help calculate the minimum length of George's stick.

The length of an integer with each section are greater than zero stick representation.

Input Format

Input data comprises a plurality of sets, each set includes two rows of data.

The first line is an integer of not more than 64, showing the section cut off after a total of how many sticks.

The second line is the length of the sections cut off after the stick, the resultant.

After the last set of data is a zero.

Output Format

For each set of data and outputs the original wooden smallest possible length, each set of data per line.

data range

The data length of each section of the stick to ensure that not more than 50.

Sample input:

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample output:

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;
    }
}
 

 



Guess you like

Origin www.cnblogs.com/kstranger/p/12240157.html