小木棍

小木棍

题目描述(洛谷1120)乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50,

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

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

思路

  1. 这题明显是一段一段拼接,要使用广搜或深搜,用广搜则必然导致时间浪费,当拼不上则应立即调整上一状态,则同级状态也应改变,用广搜明显不合适。所以深搜用递归。
  2. 因为小于等于50,首先想到了桶排序;用其他排序有些麻烦,因为需要不断排序与增删小木棍,当次数过多显然耗时过多,有sort函数也不划算。
  3. . 枚举最多可能段数或最小可能长度。不论枚举段数还是长度,都要事先把预得到的长度或段数计算出来,然后递归。自然想到,k为最小可能长度,g为最大可能段数,用当前还差n个长度的就拼好一段(也可用当前段已拼接的长度)去递归是好实现的。即dfs(int k,int n,int c);(c为当前已拼接长度) 当n=0,dfs(k,k,c+1)即可,当c=g,则由于循环顺序,答案已出。

优化(dfs)

1.显然枚举所有情况再用递归判断是否可行很浪费时间,再输入数据时统计sum,当g,k都为整数时在调用递归判断是否可行;

  1. 显然枚举所有情况再用递归判断是否可行很浪费时间,再输入数据时统计sum,当g,k都为整数时在调用递归判断是否可行;
  2. 从剩下的小木棍从大到小挑选出第一个不大于n的小木棍进行拼接,eg(挑5,有5,3,2肯定挑5),将较小较灵活组合的小木棍留下来。注意:当挑6,有5,4,2,1,有些木棍拼接需要1,所以有时要选4,2,不能因挑走5后,剩下的木棍无法拼接就终止以k为最小长度的递归判断。
  3. 当n=k时,向下一层dfs(k,n–i(当前选中木棍长度),c)返回失败时,直接向上一层返回失败。(c为已拼接好的段数)因为当前要重新开始拼接新的长度为k的一段,下一层返回失败即当前选择的这根与剩下的小木棍无法拼接成长度为k的一段,直接返回失败。
  4. 当n=i(当前选中木棍长度),向下一层dfs(k,k,c+1)返回失败时,直接向上一层返回失败。
  5. 输入数据时,记下最大的小木棍长度maxa,主函数枚举时直接从maxa枚举最小长度(枚举段数没有这样优势了)。
    大概就是这样迭代枚举长度,符合都是整数,调用dfs(flag为标记)
void work(int k,int n,int c){
	if(c==g) flag=1;
	if(!n) {work(k,k,c+1);return;}
    for(int i=50;i>0&&!flag;--i){
    	if(lenth[i]&&i<=n) {
    	    --lenth[i];
    	    work(k,n-i,c);
    	    ++lenth[i];
    	    if(n==k||n==i) return;
        }
    }
}

然而还是会超时的,洛谷数据更新了!!!!

再优化

我一直超时服了,看了dalao的代码,!!!少了一些小优化

  1. 没必要枚举长度循环到sum,而是sum/2,即 **for(int i=maxa;i<=sum/2;++i)**当迭代结束后,flag=1输出当前循环的i,否则输出sum。
  2. 还没完,范围优化,在录入数据时,记下有效数据最大值maxa,最小值mina,再向dfs中加入开始枚举的最大长度q,则
void work(int k,int n,int c,int q){
	if(c==g) flag=1;
	if(!n) {work(k,k,c+1,maxa);return;}
    for(int i=q;i>=mina&&!flag;--i){
    	if(lenth[i]&&i<=n) {
    	    --lenth[i];
    	    work(k,n-i,c,i);
    	    ++lenth[i];
    	    if(n==k||n==i) return;
        }
    }
}

对特殊数据有很大的优化效果

  1. 不使用flag,在得到结果时还要在层层返回,浪费时间,直接打印k,exit(0);否则在最后就回到了主函数直接输出sum。

  2. 其实还可以手写输入代码。

直接上源代码

#include<bits/stdc++.h>
using namespace std;
int N,sum,maxa,g,mina=69;
int tiao[55];
void read(){
    scanf("%d",&N);
    for(int i=1,j=0;i<=N;++i){
        int x;scanf("%d",&x);
        if(x>50)continue;
        sum+=x;
        if(x>maxa) maxa=x;
        if(x<mina)mina=x;
        tiao[x]++;
    }
}

void work(int k,int n,int c,int q){
	if(c==g) {printf("%d",k);exit(0);};
	if(!n) {work(k,k,c+1,maxa);return;}
    for(int i=q;i>=mina;--i)
    	if(tiao[i]&&i<=n) {
    	    --tiao[i]; work(k,n-i,c,i);++tiao[i];
    	    if(n==k||n==i) return ;
        }
}

int main(){
    read();
    N=sum>>1;
    for(int i=maxa;i<=N;++i){
        if(sum%i==0){
            g=sum/i;
            work(i,i,0,maxa);
        }
    }
    printf("%d",sum);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值