【二分】【dfs】【剪枝】Sticks HDU - 1455 【拼凑小木棍,变成多条一样长的,求最小可能的长度】

【dfs】【剪枝】Sticks HDU - 1455 【拼凑小木棍,变成多条一样长的,求最小可能的长度】

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

Input
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

Output
The output file contains the smallest possible length of original sticks, one per line.

Sample Input
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output
6
5

题意:
原来多条长度一样的木棒,将其剪短到每小条不超过5,现在要把他们再拼回去,问原长度最短是多少?

思路:

  • 求原长度最短是多少,可以二分,最小的可能是现有最长木棒的长度,最大的可能是所有木棒的和。
  • 如何check?将木棒一个个拼回去,看能不能拼完,可以得话,说明OK。拼回去的过程用dfs。
  • 每次拼一个原长度的木棍,bool dfs(int m, int x)表示还剩下m根木棍没有拼,x表示原长度拼凑后还剩下没有拼起来的长度。因为拼回去有多种可能,所以在这个过程中需要剪枝,即把无解的情况尽早排除,不往下推。

    • 首先应该尽量选择大木块进行拼凑,因为在剩余可拼凑长度m较大时,大木块更容易拼上,更容易得到解,所以可以将木块长度先排序,从长的开始拼,每拼上一个就标记它已经使用过。

    • 在dfs决策过程中,不会选择相同长度的棍子,因为这两根棍子在选择的过程中是等价的,一旦一根棍子无解,另一根也肯定无解。即在寻找匹配木棍过程中,如果找到一个木棍,它的长度和它前一个木棍长度相同,而前一个木棍没有没有被使用,那么它也一定不会被拼成功,因为它们俩是等效的。

    • dfs过程中每次选择一根棍子,一旦这根棍子无解,那么我们就可以回溯了,因为在整个答案中这根棍子迟早要选择(向前推进无望,只能回溯)。比如:
      -1、 a[i] = x
      a[i] = x,则恰好可以把这一次的剩余长度拼完,那么剩下的就是较多的小木块,如果在当前这种小木块多的情况下都无法实现,那么这种情况肯定不行,因为在这里a[i]如果被小的木块替代了,那么a[i]将被分配到下一堆,既然小木块多的情况都无法组合,那把小木块换成大木块,灵活性更低,更无法满足了。
      -2、 x = len
      x = len,说明还没有用木块来拼,在这种情况下,使用当前可用的最大木块都无法组合出len,而这些剩余的大木块迟早要被选用来拼一个len,在这种小木块多灵活性较高的情况下,都无法匹配,那么放到后面,这些大木块更无法匹配。

AC代码:

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <queue>

using namespace std;

const int MAXN  = 70;
int n, len, sum, vis[MAXN], a[MAXN];

bool cmp(int a, int b)
{
    return a > b;
}

bool dfs(int m, int x)
{
    if(m == 0 && x == 0)
    {
        return true;
    }
    if(x == 0)
    {
        x = len;
    }
    for(int i = 0; i < n; i++)
    {
        if(!vis[i] && a[i] <= x)
        {
            if(i > 0)
            {
                if(!vis[i - 1] && a[i - 1] == a[i])//将长度相同且无法匹配的树枝剪掉
                    continue;
            }
            vis[i] = 1;
            if(dfs(m - 1, x - a[i]))
                return true;
            else
            {
                vis[i] = 0;
                if(a[i] == x || x == len)
//1、 a[i] = x, 如果在当前这种小木块多的情况下都无法实现,那么这种情况肯定不行,因为在//这里a[i]如果被小的木块替代了,那么a[i]将被分配到下一堆,
//既然小木块多的情况都无法组合,那把小木块换成大木块,灵活性更低,更无法满足了。
//2、 x = len,说明使用当前可用的最大木块无法组合出len,而这个大木块迟早要被选用,在这//种小木块多灵活性较高的情况下,都无法匹配,那么放到后面,这根大木块更无法匹配。
                    return false;
            }
        }
    }
    return false;
}

int main()
{
    while(scanf("%d", &n) != EOF)
    {
        if(n == 0)
            break;
        sum = 0;
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
            sum += a[i];
        }
        sort(a, a + n, cmp);
        for(len = a[0]; len <= sum / 2; len++)
        {
            if(sum % len == 0)
            {
                memset(vis, 0, sizeof(vis));
                if(dfs(n, len))
                {
                    cout<<len<<endl;
                    break;
                }
            }
        }
        if(len > sum / 2)
            cout<<sum<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Floraqiu/article/details/81262701
今日推荐