P1120 小木棍

小木棍

题目描述

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

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

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

输入格式

第一行是一个整数 n n n,表示小木棍的个数。
第二行有 n n n 个整数,表示各个木棍的长度 a i a_i ai

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

9
5 2 1 5 2 1 5 2 1

样例输出 #1

6

提示

对于全部测试点, 1 ≤ n ≤ 65 1 \leq n \leq 65 1n65 1 ≤ a i ≤ 50 1 \leq a_i \leq 50 1ai50

代码

#include<bits/stdc++.h>
using namespace std;
int n,cnt=1,a[100],sum,len,m,ne[100];
bool used[50],ok=0;
struct cmp{
	bool operator ()(const int &a,const int &b){
		return a>b;
	}
};
void dfs(int k,int last,int rest){
	int i;
	if (rest==0){
		if (k==m){ok=1;return;}
		for (i=1;i<=cnt;i++)
			if (used[i]==0) break;
		used[i]=1;
		dfs(k+1,i,len-a[i]);
		used[i]=0;
		if(ok) return;
	}
	for (i=last+1;i<=cnt;i++){
		if (a[i]<=rest and !used[i]) break;
	}
	for(;i<=cnt;i++){
		if (used[i]) continue;
		used[i]=1;
		dfs(k,i,rest-a[i]);
		used[i]=0;
		if (ok) return;
		if (rest==a[i]) return;
		i=ne[i];
	}
}
int main(){
	cin>>n;
	for (int i=1;i<=n;i++){
		cin>>a[cnt];
		if (a[cnt]<=50){
			sum+=a[cnt];
			cnt++;
		}
	}
	cnt--;
	sort(a+1,a+1+cnt,cmp());
	ne[cnt]=cnt;
	for (int i=cnt-1;i>0;i--){
		if (a[i]==a[ne[i+1]]) ne[i]=ne[i+1];
		else ne[i]=i;
	}
	for (len=a[1];len<=sum/2;len++){
		if (sum%len!=0) continue;
		m=sum/len;
		ok=0;
		used[1]=1;
		dfs(1,1,len-a[1]);
		used[1]=0;
		if (ok) {cout<<len;return 0;}
	}
	cout<<sum;
	return 0;
}

分析

struct cmp{
	bool operator ()(const int &a,const int &b){
		return a>b;
	}
};
sort(a+1,a+1+cnt,cmp());

利用functor提高sort程序效率,比起函数更快
2.

ne[cnt]=cnt;
	for (int i=cnt-1;i>0;i--){
		if (a[i]==a[ne[i+1]]) ne[i]=ne[i+1];
		else ne[i]=i;
	}

排除等效冗余,用ne[i]表示排序后对于a[i]的最后一个数的下标
3.sum/2,选一半即可,若无解,则输出sum
4.输入时要判断是否不大于50,舍弃部分数据
5.if (sum%len!=0) continue; 排除不可能的len
6.

if (ok) return;

立即回溯,不在搜索
7.if (rest==a[i]) return;dfs(k,i,rest-a[i]);之后,rest==0一定成立,不再搜索

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值