zoukankan      html  css  js  c++  java
  • 题解 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;
    }
    
    
  • 相关阅读:
    Windows开发,关于通过写代码加载PDB的那些事
    从FreeBSD里面看到的网络协议列表,感觉可以保存一下
    听了几段《双投唐》
    ReactOS 无法显示中文的问题
    ReactOS 代码更新后的编译安装
    都什么年代了,怎么还那种德行
    假冒不伪劣
    ollvm 使用——“Cannot open /dev/random”错误的解决方法
    ollvm 编译
    C/C++ 吐槽第一期:你最讨厌的C/C++里面的数据类型是什么
  • 原文地址:https://www.cnblogs.com/Chicago/p/9920785.html
Copyright © 2011-2022 走看看