poj 1011 Sticks (经典dfs)

原题友情链接~点击此处打开~

传说中的经典dfs。果然剪枝很恶心~。不过话说理解题意也花了好长时间。

先说下题目大意:

Sample中有一个n表示短棍有多少个,下一行中依次输入这n个短棍的长度。然后告诉你说这n根短棍都是由好多根长度相同的长棍通过切割得到的,然后让你求出原来长棍的最短长度。


分析思考~

1。原来长棍的长度为initlen,那么initlen的长度范围是[maxlen,sumlen];不需要解释。

2。还已知原来长棍的长度initlen是相同的,又可以得到sumlen%initlen == 0。又是一步剪枝~

3。因为存在长度相同的棍子。那么在进行一次排序后长度相同的棍子都连续出现在数组元素中。可以通过一个tmp标记变量。若一组相同的棍子的长度不符合要求,仅对第一根进行dfs遍历。其他的直接跳过。

4。最重要的一点剪枝。我也是看别人的~~。对于某个目标InitLen,在每次构建新的长度为InitLen的原始棒时,检查新棒的第一根stick[i],若在搜索完所有stick[]后都无法组合,则说明stick[i]无法在当前组合方式下组合,不用往下搜索(往下搜索会令stick[i]被舍弃),直接返回上一层


通过这四步剪枝就可以A出了。~~~

具体实现看代码。~~ps :dfs还真不是那么好写。


 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int vMaxsize = 100;

int data[vMaxsize];

bool vis[vMaxsize];

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

bool dfs(int aimlen,int cnt,int n,int cur,int curlen)
{
    if(cur == cnt)
    {
        return true;
    }
    int tmp = -1;
    for(int i = 0 ; i < n ; i++)
    {
        if(vis[i] || data[i] == tmp)continue;
        vis[i] = true;
        if(curlen + data[i] < aimlen)
        {
            if(dfs(aimlen,cnt,n,cur,curlen+data[i]))
            {
                return true;
            }
            else/*对于长度相同 && 不符合组合情况的直接排除,不在进行dfs*/
            {
                tmp = data[i];
            }
        }
        else if(curlen + data[i] == aimlen)
        {
            if(dfs(aimlen,cnt,n,cur+1,0))
            {
                return true;
            }
            else
            {
                tmp = data[i];
            }
        }
        vis[i] = false;
        if(curlen == 0)/*所有短棍的长短都对curlen为0时不产生影响,则直接跳出*/
        {
            break;
        }
    }
    return false;
}

int main()
{
    int n,maxlen,minlen,sumlen,initlen;
    while(scanf("%d",&n) && n)
    {
        sumlen = maxlen = minlen = 0;
        memset(vis,false,sizeof(vis));
        memset(data,0,sizeof(data));
        for(int i = 0 ; i < n ; i++)
        {
            scanf("%d",&data[i]);
            sumlen += data[i];
        }
        sort(data,data+n,cmp);/*对所有短棍进行降序排列*/
        maxlen = data[0];
        minlen = data[n-1];
        bool flag = false;/*initlen的范围必然在[maxlen,sumlen]之间 && sumlen%initlen == 0 */
        for( initlen = maxlen; initlen <= sumlen - initlen ; initlen++ )/*还可证明得。若在[maxlen,sumlen-initlen]之间没有找到initlen*/
        {                                                               /*则最短长度一定为sumlen*/
            if(!(sumlen%initlen) && dfs(initlen,sumlen/initlen,n,0,0))
            {
                printf("%d\n",initlen);
                flag = true;
                break;
            }
        }
        if(!flag)
        {
            printf("%d\n",sumlen);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值