很坑爹啊,浪费了一个晚上,其实我做过poj1011的。。那时候都顺利过了。。这个简化版居然纠结那么久。。最后发现没记录当前所到木棍,一开始觉得这个小小的剪枝就没必要啦,在poj1011时我就没做这个剪枝,都轻松过了。。。TLE了无数次,才决定试着改下。。⊙﹏⊙b汗。。0ms过了。。看来真的任何一点小小的代码都不容忽视的。。好吧,继续加油~
/* zoj_1909 搜索 很经典的一道题。poj1011的简化版。。 剪枝: 1.从长到短排序,这样搜索失败时在离根部不远就结束的可能性大。 2.长度总和%4==0 3.最短木棍大于边长度 4.最后一条边不用搜 5.对相同长度木棍分类,避免搜索的时候做重复的搜索。 6.当前最长木棍必须用来组边。否则可直接退出。因为如果当前都不能用 后面也永远无法再用到。这个剪枝威力巨大。 7.记录当前所到木棍的序号,即为代码中的index。这个在此题中关键,不 记录必然TLE(我一开始就没记,每次从0开始,TLE无数次) */ #include <iostream> #include <cstdio> #include <string.h> #include <algorithm> using namespace std; struct stick { int len; int num; }s[25]; int m,ave; bool cmp( stick a,stick b ) { return a.len>b.len; } bool dfs( int rest,int sum,int index ) { int i; if( sum==ave ) return true; //剪枝4 for( i=index;i<m;i++ ) //剪枝7 { if( s[i].num!=0 && s[i].len<=rest ) { s[i].num--; if( rest==s[i].len ) { if( dfs( ave,sum-rest,0 ) ) return true; } else if( dfs( rest-s[i].len,sum-s[i].len,i ) ) return true; s[i].num++; if( rest==ave ) return false; //剪枝6 } } return false; } int main() { int n,i,j,k,t,maxi,sum; scanf( "%d",&n ); while( n-- ) { scanf( "%d",&m ); sum=0; k=0; maxi=-1; for( i=0;i<m;i++ ) { scanf( "%d",&t ); sum+=t ; for( j=0;j<k;j++ ) { if( s[j].len==t ) //剪枝5 { s[j].num++; break; } } if( j==k ) { s[k].len=t; s[k].num=1; k++; if( maxi<t ) maxi=t; } } m=k; ave=sum/4; if( sum%4!=0 || maxi>ave ) //剪枝2.剪枝3 printf( "no\n" ); else { sort( s,s+m,cmp ); //剪枝1 if( dfs( ave,sum,0 ) ) printf( "yes\n" ); else printf( "no\n" ); } } return 0; }