Stick ------ 剪枝神题
这个是自己剪得 , 我感觉已经很不错了 但是不知道哪里出了问题 一直 超时
// 根据所给答案 和 题目要求 最直观的就可以有剪枝的地方 而且 剪枝剪得越早 就越省时省力 // 好的思路也可以省不少的时间 #include<stdio.h> #include<string.h> #include<math.h> #include<iostream> #include<algorithm> #include<queue> #include<vector> #include<set> #include<stack> #include<string> #include<sstream> #include<map> #include<cctype> using namespace std; bool cmp(int a,int b) { return a>b; } int a[55],goal,visited[55],mark,result,sum,n,flag; void DFS_check() // 我感觉 前面的两个剪枝 就十分的强悍了 但是鉴于 这一道题 比较的 变态 所以 继续剪枝! { if(flag) return ; if(goal>result) return ; if(goal==result) // 加上了最后一个数字 这时候 mark 也刚刚好 所以 这个放上面 { mark++; goal=0; // 攒够 一套之后 就直接让 计数器归零 } if(mark*result==sum)// 这时候 是 刚好符合条件 // 这个值 不可能大于 sum { flag=1; } for(int i=0;i<n;i++) { if(!visited[i]) // 没有被访问过 { visited[i]=1; goal+=a[i]; DFS_check(); goal-=a[i]; visited[i]=0; while(a[i]==a[i+1]) i++; } } if(flag) return ; } int main() { while(scanf("%d",&n),n) { result=sum=0; for(int i=0;i<n;i++) { scanf("%d",&a[i]); sum+=a[i]; } sort(a,a+n,cmp); // 将 碎木棒长度 从大到小排序之后 1 :方便操作 2 :在找出答案之后 可以很快的退出 寻找 ,能省不少的时间 int len=(sum/2)+1; for(int i=a[0];i<=len;i++) // 一个剪枝 , 这个估计能省去 理论上一半的时间 的时间 { if(sum%i!=0) // 这里的 i 是假设的 原始木棒长度 // 如果总长度 不能被假设木棒长度整除的话 , 这个假设就是不成立的 . continue; memset(visited,0,sizeof(visited)); flag=goal=mark=0; result=i; DFS_check(); if(flag) // 检查 该长度 是否 是 最小的木棒长度 { result=i; //更新最小木棒长度 break; } } if(result==0) result=sum; printf("%d\n",result); } return 0; }
下面附上正确答案
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 #include<vector> 8 #include<set> 9 #include<stack> 10 #include<string> 11 #include<sstream> 12 #include<map> 13 #include<cctype> 14 using namespace std; 15 int n,sum,a[100],goal,visited[100]; 16 bool cmp(int a,int b) 17 { 18 return a>b; 19 } 20 bool DFS(int now,int index,int mark,int goal) // mark 已集齐的 原始木棒数量 goal 假设原始木棒长度 21 { 22 if(mark*goal==sum) 23 return true; 24 for(int i=index;i<n;i++) 25 { 26 if(visited[i]||(a[i]==a[i-1]&&!visited[i-1])) //如果这一个节点 已经 被用了 就不再用了 //如果这个节点和上一个节点相同 并且上一个节点 没有被使用 那么这个节点就也不会被使用了 27 continue; // 因为 出现这种情况 的原因只有一种就是 + 这个数值 now 大于了 目标值 28 if(now+a[i]==goal) 29 { 30 visited[i]=1; 31 if(DFS(0,0,mark+1,goal)) 32 return true; 33 visited[i]=0; 34 return false; 35 } 36 else 37 if(now+a[i]<goal) 38 { 39 visited[i]=1; 40 if(DFS(now+a[i],i+1,mark,goal)) // 在已经确定了一个比较大的值之后 继续向后面寻找比较小的值 41 return true; 42 visited[i]=0; 43 if(now==0) 44 return false; 45 } 46 } 47 return false; 48 } 49 int main() 50 { 51 while(scanf("%d",&n) == 1 && n) 52 { 53 int i; 54 for(i=sum=0;i<n;i++) 55 { 56 scanf("%d",&a[i]); 57 sum+=a[i]; 58 } 59 sort(a,a+n,cmp); 60 for(i=a[0];i<sum;i++)// 开始从最长的假设 61 { 62 if(sum%i) //如果不能整除的话 那一定不是 原始 平均长度 63 continue; 64 memset(visited,0,sizeof(visited)); 65 if(DFS(0,0,0,i)) 66 { 67 printf("%d\n",i); 68 break; 69 } 70 } 71 if(i==sum) 72 printf("%d\n",sum); 73 } 74 return 0; 75 }