您的位置:首页 > 其它

【洛谷】P1120 小木棍[数据加强版]

2017-08-29 22:50 204 查看
传送门

这道题目因为加强了数据,所以博客以前的题解不能够满足这道题目的时间复杂度,当然还是使用暴力,但是得多一点剪枝。

剪枝1:将木棍从大到小排序,这样搜索就可以少一些可能性。

剪枝2:从最大的一根木棍开始枚举,一直到木棍长度之和/2,因为最糟糕的情况就是每一根木棍只与自己搭配,所以这种情况在最后输出,再就是两两搭配,所以答案就是总和/2,这样可以减去差不多一半的复杂度。

剪枝3:如果前t−1个木棍都订好了,最后一根木棍想都不用想都可以搭配。

剪枝4:缩小搜索范围,但是要记得每一次s==m的时候初始化。

剪枝5:如果着一根木棍被用过了,或者是它与当前木棍之和加起来超过了我们想要的木棍总长,就不选它。

剪枝6:如果我当前的木棍和上一个木棍是一样的,而且上面一根木棍不能够凑出答案,那我这一根也不行!

剪枝7:如果我当前的长度,加上后面所有的木棍的长度都凑不出来一个总木棍长,就可以放弃这种方法。

剪枝8:如果当前状态(新的)搜索不出来一根新木棍,就反悔。

剪枝9:如果我现在这根木棍加上总和才可以凑出m,那么后面就凑不出来了!

具体看代码:

//洛谷P1120 小木棍
#include<cstdio>
#include<cstdlib>
#include<algorithm>
int n,cnt,sum,maxx,t,m;
int a[100],p[110];
bool b[100];
using namespace std;
int cmp(const int&a,const int&b) {
return a>b;
}
void dfs(int l,int s,int last) {
if(s==m) {//如果找好了一根木棍
s=0;//累加器清零
l++;//累记的以前的总木棍个数+1
last=1;//下次从第一个木棍开始搜索
}
if(l==t-1) {//这是第3个剪枝
printf("%d\n",m);exit(0);
}
for(int i=last; i<=cnt; i++) {//这是第4个剪枝
if(s+a[i]>m||b[i]) continue;//这是一个可行性剪枝[剪枝5]
if(a[i]==a[i-1]&&!b[i-1]) continue;//这是第6个剪枝
if(s+p[i]<m)return;//这是第7个剪枝[很重要!]
b[i]=1;
dfs(l,s+a[i],i+1);
b[i]=0;//这是普通搜索加回溯
if(s==0)return;//这是第8个剪枝
if(s+a[i]==m)return;//这是第9个剪枝
}
}
void init() {
int x;
for(int i=1; i<=n; i++) {
scanf("%d",&x);
if(x<=50) {
a[++cnt]=x;//重新挑选
sum+=x;//累加
maxx=max(maxx,x);//计算最大值
}
}
}
int main() {
int i,j,k;
scanf("%d",&n);
init();//读入,这里记住一定要排除50以上的!
sort(a+1,a+n+1,cmp);//第1个剪枝
for(i=n;i>=1;i--)
p[i]=p[i+1]+a[i];//计算一个后缀和,剪枝需要
for(i=maxx; i<=sum/2; i++) {//第2个剪枝
if(sum%i==0) {//如果它有成为答案的可能才搜索,不要浪费时间
t=sum/i;//这个表示以前总的木棍个数
m=i;//这是以前木棍的长度
dfs(0,0,0);//搜索
}
}
printf("%d\n",sum);//如果循环结束了程序还没有结束,就只能输出这个,见上分析
return 0;
}


这道题目很适合练习剪枝的同学们做,确实是有些复杂,但是应该有比我这个代码更快的[二分答案],希望大家能够写出来!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: