//Sticks Time Limit: 1000MS Memory Limit: 10000K POJ//

题目链接:/http://poj.org/problem?id=1011
//题目类型:搜索
/*
题目要求已经把一根整的小棍分开成若干根长度不等的小棍,让我们做的工作就是去计算,能够把这些小棍复原成n根长度相同的小棍,而且题目中所给的
小棍必须全部用上。找出能够满足以上条件的,组成之后最短的棍子长度。
这道题目是用DFS做的,之所以用这个来做是因为,当我们要组合一些小棍成为新的棍子的时候,长度必定是要相加的,而在相加的过程中
我们也要去检查是否符合我们预先设定的长度,如果小了继续加。如果大了就退出,搜索下一个去加。这是一种典型的DFS思想,一个方向搜到底,如果没有答案
再回退去搜其他方向。
看了书之后,这个问题得到了转化,我们要组合小棍肯定要把小棍的长度相加,那么最小的组合之后的小棍长度也是大于等于原来给定小棍中最大的那个,因为
我们不能再把给定的棍子的长度再改变了。问题开始转化,我们要找一个长度,使给定小棍组合相加,形成几个这样长度的小棍,而且小棍必须都用上,那么也就是说
n根的小棍长度给定,长度总和为s,我们要组成num个小棍,每个小棍的长度是sum/num。列出所有可能的情况之后我们要找出最小的那个长度是多少。


我们需要从原来给定小棍的最大的那个开始遍历,每次+1,直到这个长度等于原来给定小棍长度的总和,这是一种最坏的情况,所有小棍组成一个棍子,也是保证搜索中
一定能够有出口的条件。

DFS每次传三个参数,当前组成设定长度的棍子数,当前所 持有的待组成的棍子的长度,每次搜索的位置。
因为一旦找到了合适的长度,那么组成设定的棍子数num便成为了递归的边界条件。
如果不用组合相加,直接就找到了符合设定长度的棍子,那么棍子数+1,当前所持有的带组成的棍子长度清零,因为要组成一个棍子,必须符合设定的长度。
每次用到一个棍子的时候要标记它已经用过,这样记忆化的操作可以剪枝。
如果一次没有找到,当前的棍子长度小于设定的长度,那么我们就要继续找其他的棍子相加来构成这个规定的长度。所传参数的棍子数因为没有组成成功不变,但是
当前所 持有的待组成的棍子的长度即为你每次将棍子长度相加的结果。搜索位置也由原来的起点变为当前的位置,因为之前的已经遍历过并且使用过,接下来要加的就是后面没有使用过的。

还要说的是,如果在相加小棍长度寻找符合规定长度的过程中失败退出的话,如果现在的手中所执有的棍子长度为0,也就是没有棍子,证明这条路依然行不通了,所有的路都已经尝试过了。

最后重中之重的一步剪枝操作,我总觉得没做过一定的题目和看了书之后想不出来这种办法。
如果当前在寻找相加如果在相加小棍长度寻找符合规定长度的过程中失败的话,想继续寻找下一个小棍相加看是否符合,但是在这之前要做的操作就是
检查当前失败位置和当前位置之后的小棍长度是否相等,如果相等那么肯定不用搜索了,要跳过。所以我估计数据里面肯定有很多组是一个对的后面跟着n多错误的,但是你却一直在搜
到时候必死无疑
*/



#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int sticks[100];
bool vis[100];
int count1;
int N;
int sum;
int target;
int cmp(int a,int b)
{
    if(a>b)
        return 1;
    else
        return 0;
}
int dfs(int s,int len,int pos)
{
    int i,j;
    int flag;
    flag=(len==0?1:0);
    if(s==count1)
        return 1;
    else
        for(i=pos+1;i<=N;i++)
        {
            if(vis[i])   continue;
            if(sticks[i]+len==target)
            {
                vis[i]=1;
                if(dfs(s+1,0,0))
                    return 1;
                vis[i]=0;
                return 0;
            }
            else
                if(sticks[i]+len<target)
                {
                    vis[i]=1;
                    if(dfs(s,len+sticks[i],i))
                        return 1;
                    vis[i]=0;
                    if(flag)
                    return 0;
                    while(sticks[i]==sticks[i+1])i++;
                }

        }
        return 0;
}
int main()
{
    int i,j,k;
    while(scanf("%d",&N)&&N)
    {
        count1=0;
        sum=0;
        for(i=1;i<=N;i++)
        {
            scanf("%d",&sticks[i]);
            sum+=sticks[i];
        }
        sort(sticks+1,sticks+N+1,cmp);
        for(target=sticks[1];target<=sum;target++)
            if(sum%target==0)
            {
                count1=sum/target;
                memset(vis,0,sizeof(vis));
                if(dfs(1,0,0))
                {
                    printf("%d\n",target);
                    break;
                }
            }

    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值