zoj_1909

11 篇文章 0 订阅

很坑爹啊,浪费了一个晚上,其实我做过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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值