POJ 1011 Sticks dfs,剪枝 难度:2

http://poj.org/problem?id=1011

要把所给的集合分成几个集合,每个集合相加之和ans相等,且ans最小,因为这个和ans只在[1,64*50]内,所以可以用dfs一试

首先ans需要满足两个条件1.可以被总集合的和sum整除 2.是总集合的某个子集的和 对于条件1,可以通过试除遍历 对于条件2,可以通过dp预筛选,这两个花费时间都不大

接着搜索能不能划分成集合之和恰为ans的若干集合,

1. 可以从大向小找,因为大的更不灵活,而且所有的元素都需要取到

2.比如对于5,4,2,2,1 使用第一个2,向下找,没有找到答案,不用第一个2之后,也不需要再找第二,第三个2了

如果之前找过相同的数字了,那么之后就不用再找相同的数字

3.如果这次数字刚好组合成为一个ans,那么能不能组成由剩下的数字决定,也就是已经得到了一个新集合和为ans了,剩下的能够组成若干和为ans的集合则可以取ans,否则不能取ans

#include <cstdio>

#include<cstring>

#include <algorithm>

using namespace std;

int a[64],n;

bool cmp(int a,int b){return a>b;}

bool reachable[64*51];

bool used[64];

bool dfs(int s,int tmp,int sub){

     //   printf("s:%d tmp:%d sub:%d\n",s,tmp,sub);

        used[s]=true;

        tmp+=a[s];

        if(tmp>sub){// 不可能出现

                puts("ERROR");

                used[s]=false;

                return false;

        }

        if(tmp==sub){//恰好成为一个和为ans的集合

                for(int i=0;i<n;i++){

                        if(!used[i]){

                                if(dfs(i,0,sub))return true;//dfs新的集合,直接返回剩下的元素能不能组合成若干和为ans的集合

                                else {

                                        used[s]=false;

                                        return false;

                                }

                        }

                }

                return true;//所有元素都被使用了

        }

        int f=-1;

        for(int i=s+1;i<n;i++){

                if(f==a[i]||used[i]){continue;}//如果这个状态的这个函数已经找过相同的数

                if(tmp+a[i]==sub){//直接返回剩下的元素的组合状态

                        if(dfs(i,tmp,sub))return true;

                        else {

                                used[s]=false;

                                return false;

                        }

                }

                else if(tmp+a[i]<sub){//可以试着添加进这个集合

                        if(dfs(i,tmp,sub))return true;

                }

                f=a[i];

        }

        used[s]=false;

        return false;

}

int main(){

        while(scanf("%d",&n)==1&&n){

                int sum=0,mx=0;

                memset(reachable,false,sizeof(reachable));

                for(int i=0;i<n;i++){

                        scanf("%d",a+i);

                        sum+=a[i];

                        mx=max(a[i],mx);

                }

                reachable[a[n-1]]=true;

                reachable[0]=true;

                sort(a,a+n,cmp);//从大到小搜索

                for(int i=n-2;i>=0;i--){

                        for(int j=0;j<sum&&j+a[i]<=sum;j++){

                                if(reachable[j]){

                                        reachable[j+a[i]]=true;//reachable[i]=true i可以被总集合中的某些元素加和得到

                                }

                        }

                }

                bool fnd=false;

                for(int i=mx;i<sum;i++){

                        if(sum%i==0&&reachable[i]){

                                memset(used,0,sizeof(used));

                                if(dfs(0,0,i)){

                                        printf("%d\n",i);

                                        fnd=true;

                                        break;

                                }

                        }

                }

                if(!fnd)printf("%d\n",sum);

        }



        return 0;

}

  

你可能感兴趣的:(poj)