HOJ 1956 Square(DFS+剪枝)
看到题目很容易想到要用DFS,但写出来之后发现TLE了。以为要用其它的算法,百度了下之后发现这题的考点就在于剪枝。
刚开始我只用了两个剪枝:各木棍之和是否能被4整除;是否有木棍的长度大于正方形的边长。
但事实证明还不够。关键的两个剪枝在DFS里面。具体看代码及注释吧。
注意:每一次递归进去,开始搜索的位置的选择,经常是可以进行重要优化的!
//DFS+剪枝优化 #include <iostream> #include <cstdio> #include <memory.h> #include <algorithm> using namespace std; int numbers[25]; bool used[25]; int side; int num; bool cmp(int a,int b) { return a>b; } bool dfs(int tsum,int cur,int leave) //tsum:这个边已经组了多长;cur:正在组的是第几条边;leave:组这条边已经用了几根棒 { if(tsum==side) { cur++; tsum=0; } if(cur==4)return true; for(int i=leave; i<num; i++) //i从leave开始。因为对棒已经降序排过序了,再搜索只需要对leave之后的棒进行搜索,它之前的肯定是不会符合的。 { if(i&&numbers[i]==numbers[i-1]&&!used[i-1])//相邻的相等的前者没有加入,则这个也不行。 continue; if(used[i]==0) { if(tsum+numbers[i]<side) { used[i]=1; if(dfs(tsum+numbers[i],cur,i+1))return true; //搜索的起始点很重要。i+1传入了下层递归搜索的起始点 used[i]=0; } if(tsum+numbers[i]==side) { used[i]=1; if(dfs(0,cur+1,0)) return true; used[i]=0; } if(tsum==0)break; } } return false; } int main() { int t; scanf("%d",&t); while(t--) { int sum=0; scanf("%d",&num); for(int i=0; i<num; i++) { scanf("%d",&numbers[i]); sum+=numbers[i]; } if(sum%4!=0) //从全局剪枝。 { printf("no\n"); continue; } side=sum/4; sort(numbers,numbers+num,cmp); //按降序排序。这样保证了只需要在搜索的起始点后面找棒,起始点前面的棒就不用再搜了。 if(numbers[0]>(side)) //从全局剪枝。 { printf("no\n"); continue; } memset(used,0,sizeof(used)); if(dfs(0,1,0)) { printf("yes\n"); } else printf("no\n"); } return 0; }