赞
踩
参考资料
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 50 50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
第一行是一个整数
n
n
n,表示小木棍的个数。
第二行有
n
n
n 个整数,表示各个木棍的长度
a
i
a_i
ai。
输出一行一个整数表示答案。
9
5 2 1 5 2 1 5 2 1
6
对于全部测试点, 1 ≤ n ≤ 65 1 \leq n \leq 65 1≤n≤65, 1 ≤ a i ≤ 50 1 \leq a_i \leq 50 1≤ai≤50。
定义cnt
表示原始木棍的数量,len
表示每根原始木棍的长度,sum
表示木棍的总长度。
定义状态(num, pre, last)
,其中num
表示当前正在拼的原始木棍的编号,pre
表示上一个使用的小木棍的编号,last
表示当前原始木棍的剩余长度。
last
为0,说明当前原始木棍已经拼接完成。此时,如果有num
和cnt
相等,说明所有木棍拼接成功;如果num
和cnt
不相等,则继续拼接下一个原始木棍。last
不为0,则选择尚未使用的小木棍来拼接该原始木棍。优化1:仅需要考虑len <= sum/2
的情况。
优化2:当前状态为:完成了某个原始木棍的拼接,准备进入下一个原始木棍的拼接。在这一状态下,我们选取一根最长的、尚未使用过的小木棍来进行拼接,如果拼接失败,则无需再考虑其他木棍,直接从当前状态返回。这是因为,所有未使用过的小木棍最终都要被用来拼接成原始木棍,所以选择先从哪根小木棍开始都是一样的。也就是说,当前状态并不能导出拼接成功的状态。
优化3:当前状态为:尚未完成该原始木棍的拼接,且选择了一个长度恰好为last
的小木棍。如果上述选择导致拼接失败,则无需再考虑其他小木棍,直接从当前状态返回。这是因为,如果当前状态能导出一个拼凑完成的状态,那么这个长度为last
的木棍在之后也必须要被使用。显然,在剩余小木棍较多的当前状态下,选择长度为last
的小木棍都不能完成拼接,在后面剩余小木棍更少的状态下,度为last
的小木棍一定也不能完成拼接。也就是说,当前状态并不能导出拼接成功的状态。
优化4:使用二分查找的方法,找到第一个长度<= last
的小木棍的编号,然后从该编号开始选择。需要特别注意的是,二分查找的左边界可以优化为pre+1
。
优化5:预处理Next[x]
数组,存储第一个长度< x
的小木棍的下标。
#include<bits/stdc++.h> #define maxn 70 using namespace std; int n; int a[maxn]; int sum = 0; int M = 0; int m = 0; bool vis[maxn]; int Next[maxn]; bool flag = false; int cnt, len; bool cmp(int x, int y){ return x>y; } //num:当前正在拼的是第几个原木棍 //pre:上一个使用的小木棍的编号 //last:拼完这个原木棍,还需要的长度 void dfs(int num, int pre, int last){ //当前原木棍拼成功了 if(last==0){ //所有木棍拼凑成功 if(num==cnt){ flag = true; return; } else{ for(int i=1;i<=n;i++){ if(vis[i]) continue; vis[i] = true; dfs(num+1, i, len-a[i]); vis[i] = false; if(flag) return; return; //优化2 } } } else{ //二分查找第一个长度小于剩余长度的小木棍 int L=pre+1, R=n, mid, pos=n; while(L<=R){ mid = (L+R)>>1; if(a[mid]<=last){ pos = mid; R = mid-1; } else{ L = mid+1; } } for(int i=pos;i<=n;i++){ if(vis[i]) continue; vis[i] = true; dfs(num, i, last-a[i]); vis[i] = false; if(flag) return; if(last==a[i]) return; if(a[i]==m) continue; //优化3 else i = Next[a[i]]-1; } } } int main(){ memset(vis, 0, sizeof(vis)); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; sum += a[i]; } sort(a+1, a+n+1, cmp); M = a[1]; m = a[n]; //预处理next int temp = a[1]; for(int i=2;i<=n;i++){ if(a[i]!=temp){ Next[temp] = i; temp = a[i]; } } for(int k=M;k<=sum/2;k++){ if(sum-(sum/k)*k!=0) continue; flag = false; vis[1] = true; cnt = sum/k; len = k; dfs(1, 1, k-a[1]); vis[1] = false; if(flag){ cout<<k; return 0; } } cout<<sum; return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。