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
",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
",i);
                                        fnd=true;
                                        break;
                                }
                        }
                }
                if(!fnd)printf("%d
",sum);
        }

        return 0;
}

  

原文地址:https://www.cnblogs.com/xuesu/p/4349227.html