Sticks(poj1011/uva307)
题目大意:
乔治有一些碎木棒,是通过将一些相等长度的原始木棒折断得到的,给出碎木棒的总数和各自的长度,求最小的可能的原始木棒的长度;(就是将一些正整数分组,每组加起来和相等,使和尽可能小)
一开始做poj 32ms过,但uva3000 ms 都超时。。。而且poj discuss里给出了一组bT数据,最后uva 0.2sac的代码也跑了3 。4秒左右。discuss里的大牛据说什么奇偶性剪枝0.01ms过,可惜搜了半天也没找到具体方法。这题就这样过好了。。(参考各种空间博客才艰难ac。不过此题实在是太经典了,搜索学习剪枝必做)
对于这道题而言,剪枝的策略一般有下面6个:
①先将木棒长度从大到小进行排序,这样便于后面的选择和操作,是后面一些剪枝算法的前提。
②在枚举原木棒长度时,枚举的范围为max与sum/2之间,如果这个区间内没有找到合适的长度,那么最后原木棒的长度只能是sum。
③枚举的原木棒的长度只能是sum的约数。
④在深搜过程中,如果当前木棒和前一个木棒的长度是一样的,但是前一个木棒没有被选上,那么这个木棒也一定不会被选上。
⑤在深搜过程中,如果当前是在拼一根新木棒的第一截,但如果把可用的最长的一根木棒用上后不能拼成功的话,那么就不用再试后面的木棒了,肯定是前面拼的过程出了问题。
⑥在深搜过程中,如果当前可用的木棒恰好能补上一根原木棒的最后一截,但用它补上之后却不能用剩下的木棒完成后续的任务,那么也不用再试后面的木棒了,肯定是前面拼的过程出了问题。
参考博客:http://www.cnblogs.com/staginner/archive/2011/09/08/2171329.html
我的AC 代码:(剪枝见注释)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,max_len,min_len,sum,ans,num,flag;
int a[10000],v[10000];
bool cmp(const int &x,const int &y)
{
return !(x<y);
}
void search(int step,int from,int cur)//step表示当前去凑的是第几根原始棒,order表示当前用了多少根碎棒子来凑第step根
{
//printf("%d %d\n",step,cur);
if (cur==ans)
{
if (step==num)
{
flag=1;
return;
}
int k;
for (k=1;k<=n && v[k];k++);
v[k]=1;
search(step+1,1,a[k]);
v[k]=0;
return;
}
for (int i=from;i<=n;i++)
{
if (v[i])
continue;
if (cur+a[i]>ans)
continue;
if (a[i]==a[i-1] && !v[i-1])
continue;//剪枝3:上一根棒和目前这根一样并且没用过,那么用这一根去凑和用上一根去凑结果一样都不能出解
//printf("kk %d %d\n",cur,cur+a[i]);
int t=a[i]+cur;
if (t!=ans && t+min_len>ans)
continue;
v[i]=1;
search(step,i+1,t);
v[i]=0;
if (t==ans)
return;
if (flag || cur==0)//剪枝4:如果当前用了1根碎棒子来凑,枚举了第一根碎棒不能出解那么跳出
return;
}
return;
}
int main ()
{
while (scanf("%d",&n)==1)
{
if (n==0)
break;
v[0]=1;
a[0]=210000000;
max_len=-1;
min_len=21000000;
sum=0;
flag=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]>max_len)
max_len=a[i];
if (a[i]<min_len)
min_len=a[i];
sum+=a[i];
}
sort(a+1,a+n+1,cmp);
for (ans=max_len;ans<=sum/2;ans++)//剪枝1:答案的下界为最长的棒,上界为长度总和
{
if (sum%ans!=0)
continue;//剪枝 2:原始棒长度是总长度的约数
for (int k=1;k<=n;k++)
v[k]=0;
num=sum/ans;
search(1,1,0);
if (flag)
{
printf("%d\n",ans);
break;
}
}
if (!flag)
printf("%d\n",sum);
}
return 0;
}
|
Every day is meaningful, keeping learning!