zoj_1909
来源:互联网 发布:php防cc攻击代码 编辑:程序博客网 时间:2024/06/06 08:48
很坑爹啊,浪费了一个晚上,其实我做过poj1011的。。那时候都顺利过了。。这个简化版居然纠结那么久。。最后发现没记录当前所到木棍,一开始觉得这个小小的剪枝就没必要啦,在poj1011时我就没做这个剪枝,都轻松过了。。。TLE了无数次,才决定试着改下。。⊙﹏⊙b汗。。0ms过了。。看来真的任何一点小小的代码都不容忽视的。。好吧,继续加油~
/*zoj_1909 搜索很经典的一道题。poj1011的简化版。。剪枝:1.从长到短排序,这样搜索失败时在离根部不远就结束的可能性大。2.长度总和%4==03.最短木棍大于边长度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;}