程序设计实习MOOC/第十三周编程作业/B:木材加工(NOIP 2004)

题目:B:木材加工
总时间限制: 1000ms 内存限制: 65536kB
描述
木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头,需要得到的小段的数目是给定了。当然,我们希望得到的小段越长越好,你的任务是计算能够得到的小段木头的最大长度。
木头长度的单位是厘米。原木的长度都是正整数,我们要求切割得到的小段木头的长度也要求是正整数。
输入
第一行是两个正整数N和K(1 ≤ N ≤ 10000, 1 ≤ K ≤ 10000),N是原木的数目,K是需要得到的小段的数目。
接下来的N行,每行有一个1到10000之间的正整数,表示一根原木的长度。
输出
输出能够切割得到的小段的最大长度。如果连1厘米长的小段都切不出来,输出"0"。
样例输入
3 7
232
124
456
样例输出
114
解题思路:将所有原木从小到大排列,分别按它们的长度作为小段木头长度(所以小段木头长度会是增加的,得到的小段木头数也会减少的),直到得到的小段木头数比k要小(若一开始就比k小,则小段木头长度从第一个原木长度往下减),这样得到了此时对应的原木长度,在该长度基础上,往下每次减一,直到找到第一个长度,使得小段木头数大于等于K,这就是最大小段木头长度(或者减到末尾为0的边界情况,就赋值成0)。
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    int n, k, length, no, num;
    cin >> n >> k;
    int a[n];
    for(int i = 0; i < n; i++)
        cin >> a[i];
    sort(a, a + n);//将木材按长度从小到大排好序
    for(int i = 0; i < n; i++)
    {
        //分别以i号木材原长为小段木头长度,直到切割出来的小段木头数目小于k(需要的小段木头数目)
        length = a[i]; 
        no = i;
        num = 0;
        for(int j = 0; j < n; j++)
            num += a[j]/length;
        if(num < k)//切割出来的小段木头数目小于k
        {
            //no中记录了是以哪根木材原长为小段木头长度,使得切出来的小段木头数目小于k, 
            //方便之后从“该长度-1”开始枚举各个小段木头长度, 
            //直到找到第一个“切割出来的小段木头数目大于等于k”的length

            break;//如果是no=i为0时,跳出的,那么就说明小段木头长度比最小的木材原长要小,所以有length = 0的情况 
        }
    }
    if(no == n - 1)//会是正常for循环结束或者break跳出 
    {
        num = 0;
        for(int j = 0; j < n; j++)
            num += a[j]/length;//length就等于a[no],即a[n - 1] 
        if(num >= k)//此时,length为最大木材原长,都能产生足够的小段木头数目,则直接输出 
        {//正常for循环结束跳出 
            cout << length <<endl;
            return 0;//程序直接结束 
        }
        //否则继续执行下面的程序 
    }
    
    for(length = a[no] - 1; length > 0; length--)
    {
        num = 0;
        for(int j = 0; j < n; j++)
            num += a[j]/length;
        if(num >= k)
            break;
    }
    
    if(length < 0)//length = a[no] - 1可能导致length值小于0,此时统一处理,length赋值成0 
        length = 0;
    cout << length <<endl;
    
    system("pause");
    return 0;
} 

上面的方法超时了,而下面的暴力穷举方法(数值不大,所以可以这么做!)居然通过了,估计是(1)多花时间在第一个for循环了,而第二个for循环也没有减少很多情况,这很可能也是测试用例的问题,使得上面的方法两个for循环都执行很久才跳出来;(2)sort函数给a数组排序耗时很多。不过平均情况,上面的方法思想是更好的!

//暴力穷举法
#include<iostream>
using namespace std;

int main()
{
    int n, k, length, num;
    cin >> n >> k;
    int a[n];
    for(int i = 0; i < n; i++)
        cin >> a[i];
    for(length = 10000; length > 0; length--)
    {
        num = 0;
        for(int j = 0; j < n; j++)
            num += a[j]/length;
        if(num >= k)
            break;
    }
    cout << length <<endl;
   
    system("pause");
    return 0;
} 
我看了下NOIP2004年的答案,估计排序耗时很多,所以没有用第一种方法,而是在我暴力穷举法上优化了很多,主要分为两点(1)一边输入一边判断来找到木材原长最大的值,记为max,作为for循环的起始值,而不是10000;(2)每次折半查找,即length变为max/2,若此时的length仍不能使得小段木头数大于等于K,则length变为(1/4)*max,否则,length变为(3/4)*max,如此折半查找下去,直到找到结果或边界情况。

### 回答1: 可以使用辗转相除法来求两个正整数m和n的最大公约数,具体步骤如下: 1. 如果m小于n,则交换m和n的值,保证m大于等于n。 2. 用m除以n,得到余数r。 3. 如果r等于0,则n就是最大公约数。 4. 如果r不等于0,则用n除以r,得到余数r1。 5. 重复步骤3和4,直到余数为0为止,此时n就是最大公约数。 下面是一个Python实现: def gcd(m, n): if m < n: m, n = n, m while n != 0: r = m % n m, n = n, r return m print(gcd(12, 18)) # 输出6 ### 回答2: 最大公约数是指能够同时整除两个数的最大的正整数,通常用gcd(m,n)表示。求两个正整数m和n的最大公约数的方法有多种,比如欧几里得算法(也称辗转相除法)、更相减损法、质因数分解法等。 其中,欧几里得算法是最常用的方法之一。其基本思是:设m>n,用n去除m,令r为所得余数(0≤r<n),若r=0,则n为最大公约数;否则,用n去除r,再得到余数,重复这个过程,直到余数为0为止,最后的除数即为最大公约数。 以下是用Python语言实现欧几里得算法的函数代码: def gcd(m, n): while n != 0: r = m % n m = n n = r return m 其中,m和n为函数输入的两个正整数,r为余数。在while循环中,如果余数不为0,则用n去除r,并将m和n分别更新为n和r,继续进行下一轮运算;如果余数为0,则此时的n即为最大公约数,直接返回即可。 这个函数可以用来求任意两个正整数的最大公约数,具有普适性和实用性,是编程中常用的工具函数之一。 ### 回答3: 求两个正整数m和n的最大公约数,是计算机程序中经常需要的一个问题。为了编写这样一个函数,需要先了解最大公约数的概念和计算方法。 最大公约数,简称“最大公因数”,是指两个或多个整数共有约数中最大的一个数。例如,12和8的最大公约数是4,因为它们的公有约数有1、2、4,其中最大的是4。 计算两个正整数m和n的最大公约数,有多种方法。其中最常用的是欧几里得算法,也称为辗转相除法。该算法的基本思是,用较大数除以较小数,然后用余数去除较小数,如此重复操作,直到余数为0,此时较小数即为两数的最大公约数。 下面给出一个用Python语言实现的求最大公约数的函数: ```python def gcd(m, n): if m < n: m, n = n, m while n != 0: r = m % n m, n = n, r return m ``` 其中,`gcd`表示最大公约数,输入参数为m和n。第一行用if语句进行判断,确保m大于等于n。接着,用while语句进行循环,每次将较大数m除以较小数n并得到余数r,再令m=n、n=r,如此进行下去,直到余数为0。这时,循环停止,说明较小数n即为m和n的最大公约数,将其返回即可。 该函数还有一个小技巧,即在开始判断m和n的大小关系之前先交换m和n的值,这样可以避免在求余数过程中出现负数,保证程序的正确性和稳定性。 总之,求两个正整数m和n的最大公约数,是编写程序中的一个常见问题。只要掌握了欧几里得算法的基本思和实现方法,就能轻松编写出一个高效、稳定的最大公约数函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值