poj1011Stick(dfs+剪枝)

题目链接:http://poj.org/problem?id=1011

Dfs中状态的转移是重点
1.分析问题
问题:将n个部分拼接成sum/h个长度为h的木棍。部分分别为stk[i]
2.分析解状态
r=sum/h
因为h是固定的,不作为状态
初始状态:
取了0个部分。剩n个部分,正在拼的木棍有h还未拼完,剩r个木棍要拼
取一个部分,还剩n-1个部分,正在拼的木棍还剩h-stk[i]还未拼完,还剩r个木棍要拼
设正在拼的木棍还剩hr  
取k个部分,拼完一根木棍,还剩n-k个部分,正在拼的木棍还有0还未拼完,还hr-1个木棍要拼
设剩下rs木棍要拼,还剩nr个部分
解状态:
拼完了r根木棍,还剩0个部分,正在拼的木棍有0还未拼完,还剩0个木棍要拼
3.写出状态转移dfs(剩n-k个部分,还有hr个部分要拼,还剩rs个木棍要拼)
dfs(n,h,r)->dfs(nr,hr,rs)->dfs(0,0,0)    (hr>stk[next])
 
dfs(nr,hr,rs)表示在该状态时,能否拼成功,返回值为bool型
当nr==0&&hr==0&&rs==0
则返回true
当nr!=0&&hr==0&&rs!=0
此时说明拼完了一根木棍,
return dfs(nr-1,h,rs-1)
当nr!=0&&hr!=0&&rs!=0
for循环选择了一个部分i以后
return dfs(nr-1,hr-stk[i],rs)
 
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
/*
对木棍的长度h有三个限制
1:所有parts的最大长度maxL<=h  且h<=parts长度和sum 
2:h|sum
3:h<=50 
显然所有的限制之中最重要的是找到sum的因子
因子还要大于等于maxL且小于等于50(暂时先不管这个) 
*/
const int maxn=65;
int n,sum,maxL;
int stk[maxn];
int h[maxn];
bool vis[maxn];
bool cmp(int a,int b){
    return a>b;
}

/*剪枝有四
1:
如果选择前面的部分之后不成功(vis[i-1]==0),而当前项又与前面项相同(stk[i]==stk[i-1]),则说明该项不可能,跳过(前提是i!=1) 
2:
如果部分i是一根木棒的第一块,那么若i不可取,不需要去遍历其他的数来替换部分i,可直接跳过 
3:
如果用部分i去填补一根木棍,正好使其拼接完成,但是dfs表明选择部分i之后不能拼接, 则不必找其他部分来替换,直接将失败反馈给前一个部分的选择 
4:
一根木棍从前到后的每一块都要保证长度从大到小,即当前选择的块不能比前一块大 
5:
如果只剩一根木棍还没拼,那肯定成功 
*/ 
bool dfs(int nr,int hr,int rs,int h){//dfs(剩下的部分数,木棍剩下未拼接的长度hr, 剩下还没拼的木棍数rs,木棍的长度h) 
    if(nr==0&&hr==0&&rs==0) 
        return true;
    else if(nr!=0&&hr==0 )//说明已经拼接完一个木棍了,接下来要拼接新的 
    {
        hr=h;
        rs--;
    }
    else if (rs==1)
        return true;
    //都没有的话,说明正在选择可取部分来填充木棍剩余的长度 
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]&&hr>=stk[i])//判断是否可取  
        {    
            vis[i]=1;
            if(i!=1&&!vis[i-1]&&vis[i-1]==vis[i])
                continue;
            if(dfs(nr-1,hr-stk[i],rs,h))//如果取这个部分可以成功 
                return true;
            else//否则,部分i不可取 
            {     
                vis[i]=0;
                if(hr==stk[i]||hr==h)//拼接完一个木棍之后,选择下一个木棍的第一块,若第一块不可取,则直接退出,去修改上一个木棍 
                    return false;
            }
        }
    }//遍历了所有可取部分,没有一个能成功的,则记得返回失败
    return false; 
}

int main (){
    while(cin>>n)
    {
        if(!n)
            break;
        sum=maxL=0;
        for(int i=1;i<=n;i++)
        {
            cin>>stk[i];
            if(maxL<stk[i])
                maxL=stk[i];
            sum+=stk[i];
        }
        //找因子 
        memset(h,0,sizeof(h));
        int cur=1;
        if(n==1)//注意考虑n==1的情况
        {
            cout<<sum<<endl;
            continue;
        }
        else{
            for(int i=1;i*i<=sum;i++)        //h中包含小于maxL的因子 
                if(sum%i==0) 
                    h[cur++]=i;
            for(int i=cur-1;i>0;i--) 
                h[cur++]=sum/h[i];    //h中的因子元素从小到大 
        }
        sort(stk+1,stk+n+1,cmp); //从大到小
        for(int i=1;i<cur;i++) //遍历所有的因子 
        {
            memset(vis,0,sizeof(vis));
            if(h[i]<maxL)
                continue;
            if(dfs(n,h[i],sum/h[i],h[i]))//如果这个木棍长度可以被拼接 
            {
                cout<<h[i]<<endl;
                break;
            }
        }//一定有一个因子能够满足(sum自身) 
    }
    return 0;
} 
View Code

 

posted @ 2014-02-17 13:13  neverchanje  阅读(199)  评论(0编辑  收藏  举报