题解 P1120 小木棍 [数据加强版]

Posted chicago

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P1120 小木棍 [数据加强版]相关的知识,希望对你有一定的参考价值。

题面

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

题意

有n段同样长的木棍,现在将这n段木棍随意分段(保证每段长度不超过50)。乔治比较闲,又想把它拼回原始木棍,但是又比较智障,忘了原来有多少根,长度是多少。

给出每段的长度,求出木棍最小可能长度。(使n尽量大,又要保证每段都拼上)

题解

题目看上去很简单,也很好想到这是一个搜索题,但是拿到后非常难以无法下手,数据的加强非常容易使这题超时。这是一道剪枝的经典题目

从最优性方面分析,可以做以下两种剪枝:

  1. 设所有木棍的长度和是sum,那么原长度(也就是需要输出的长度)一定能够被sum整除,不然就没法拼了,即一定要拼出整数根。

  2. 木棍原来的长度一定大于等于所有木棍中最长的那根。

综合上述两点,可以确定原木棍的长度len在最长木棍的长度与sum之间,且sum能被len整除。

所以,在搜索原木棍的长度时,可以设定为从截断后所有木棍中最长的长度开始,每次增加长度后,必须能整除sum。这样可以有效地优化程序。

从可行性方面分析,可以再做以下七种剪枝:

  1. 一根长木棍肯定比几根短木棍拼成同样长度的用处小,即短小的可以更灵活组合,所以可以对输入的所有木棍按长度从大到小排序。

  2. 在截断后的排好序的木棍中,当用木棍i拼合原始木棍时,可以从第i+1后的木棍开始搜。因为根据优化(1),i前面的木棍已经用过了。

  3. 用当前最长长度的木棍开始搜,如果拼不出当前设定的原木棍长度len,则直接返回,换一个原始木棍长度len。

  4. 相同长度的木棍不要搜索多次。用当前长度的木棍搜下去得不出结果时,用一支同样长度的还是得不到结果,所以,可以提前返回。

  5. 判断搜到的几根木棍组成的长度是否大于原始长度len,如果大于,没必要搜下去,可以提前返回。

  6. 判断当前剩下的木棍根数是否够拼成木棍,如果不够,肯定拼合不成功,直接返回。

  7. 找到结果后,在能返回的地方马上返回到上一层的递归处。

代码

这道题的思想不仅不简单,对代码能力也是一种考验。

#include<bits/stdc++.h>
using namespace std;

const int maxx = 66;
int n,maxn = -maxx,minn = maxx;
int tong[maxx];

void dfs( int res , int sum , int target , int p ) {
    if( res == 0 ) 
    {
        printf("%d", target  );
        exit( 0 );
    }
    
    if( sum == target ) 
    {
        dfs( res - 1 , 0 , target , maxn );
        return;
    }
    
    for( int i = p ; i >= minn ; i -- ) 
    { 
        if( tong[ i ] && i + sum <= target ) 
        {
            tong[ i ] -- ;
            dfs( res , sum + i , target , i );
            tong[ i ] ++ ;
            if ( sum == 0 || sum + i == target);
                break;
        }
    }
    
    return;
}

int main(int argc, char const *argv[])
{
    scanf("%d",&n);
    
    int x,cnt = 0,sum;

    while(n--)
    {
        scanf("%d",&x);
        if(x <= 50)
        {
            cnt++;
            tong[x]++;
            sum += x;
            maxn = max(maxn,x);
            minn = min(minn,x);
        }
    }

    x = sum/2;

    for(int i = maxn;i <= x;i++)
    {
        if(sum%i == 0)
        {
            dfs(sum/i,0,i,maxn);
        }
    }

    printf("%d",sum);

    return 0;
}

以上是关于题解 P1120 小木棍 [数据加强版]的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1120 小木棍 [数据加强版]

luogu P1120 小木棍 [数据加强版]

P1120 小木棍 [数据加强版]

P1120 小木棍 [数据加强版]

P1120小木棍

小木棍 [数据加强版]