题目地址:点击打开链接
题意:小明拿来几根相同长度的棍子,然后把这些棍子截成好几节,问最后能拼成几根长度相同的棍子(要求这些棍子的长度最小)
思路:和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,这样不就是把时间搞的更长了么