HDU 1455 Sticks(回溯,减枝很巧妙)

来源:互联网 发布:上海巴斯德研究所 知乎 编辑:程序博客网 时间:2024/06/06 07:33

题目地址:点击打开链接

题意:小明拿来几根相同长度的棍子,然后把这些棍子截成好几节,问最后能拼成几根长度相同的棍子(要求这些棍子的长度最小)

思路:和HDU1518相似,可以看本博客,那道题解写的比较详细,这不过这道题得在那道题上多加几个减枝才能A

AC代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <queue>#include <stack>#include <map>#include <cstring>#include <climits>#include <cmath>#include <cctype>using namespace std;int a[70];bool flag;int n,l;int length;int visit[70];void dfs(int start,int sum,int now){    int i;    if(flag)        return;    if(sum == l)    {        flag = true;        return;    }    for(i=start; i<n; i++)    {        if(!visit[i])        {            if(now + a[i] == length)            {                visit[i] = 1;                dfs(0,sum+1,0);                visit[i] = 0;            }            else if(now + a[i] < length)            {                visit[i] = 1;                dfs(i+1,sum,now+a[i]);                visit[i] = 0;                if(now == 0)//减枝1                    return;                while(a[i] == a[i+1])//减枝2,相同长度的就不用再搜了                    i++;            }        }    }}int main(){    int i;    while(scanf("%d",&n) && n)    {        flag = false;        int sum = 0;        for(i=0; i<n; i++)        {            scanf("%d",&a[i]);            sum += a[i];        }        sort(a,a+n);        for(i=a[n-1]; i<=sum; i++)//最后组成的边肯定大于等于最长边,枚举边比较好,枚举条数不好,原因看超时代码下面的解释        {            if(sum % i == 0)            {                memset(visit,0,sizeof(visit));                 l = sum  / i;                 length = i;                 dfs(0,0,0);                 if(flag)                 {                     printf("%d\n",i);                     break;                 }            }        }    }    return 0;}

因为这道题只求可能情况,不是求种类数,减枝方法还是挺多的(1)只要求可能情况,所以可以提前排序(由大到小或者由小到大都无所谓),每次只选它后面的,举个例子啊,1,6,2,5;一看边长就是7,从1搜到6,或者从6搜到1,都能说明这条边存在,所以只搜1到6就可以了,然后再看减枝1,这个特别巧妙,我再举个例子啊,边长为1,6,9,2,4,10;这个一看每条边就是16,减枝1之所以成立,是因为每条边都会用,所以当目前长度为0时选一个就行,而当长度为0时,选一个可能构不成边,例如这个例子,按选一个的思想,他会选1,4,6,显然是错误的,所以第一个没有组合情况,以后的都会有组合情况

超时代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <queue>#include <stack>#include <map>#include <cstring>#include <climits>#include <cmath>#include <cctype>using namespace std;int a[70];bool flag;int n,l;int length;int visit[70];void dfs(int start,int sum,int now){    int i;    if(flag)        return;    if(sum == l)    {        flag = true;        return;    }    for(i=start; i<n; i++)    {        if(!visit[i])        {            visit[i] = 1;            if(now + a[i] == length)            {                dfs(0,sum+1,0);            }            else if(now + a[i] < length)            {                dfs(i+1,sum,now+a[i]);            }            visit[i] = 0;        }    }}int main(){    int i,j;    while(scanf("%d",&n) && n)    {        flag = false;        int sum = 0;        int min1 = 1000000000;        for(i=0; i<n; i++)        {            scanf("%d",&a[i]);            sum += a[i];            if(a[i] < min1)                min1 = a[i];        }        sort(a,a+n);        for(i=sum; i>=min1; i--)        {            if(sum % i == 0)            {                memset(visit,0,sizeof(visit));                l = i;                length =  sum / i;                for(j=0; j<n; j++)                {                    if(a[j] > length)                        break;                }                if(j == n)                {                    dfs(0,0,0);                    if(flag)                    {                        printf("%d\n",length);                        break;                    }                }            }        }    }    return 0;}


其实我这个代码写的还是比较逗的,最小值排序一下就能出来,我却特意求了一下,然后枚举的条数,结果里面多加了一个for循环减枝,并且for循环的范围大,枚举边的范围是从最大边到sum,而枚举条数是从最小边到sum,这样不就是把时间搞的更长了么

0 0
原创粉丝点击