2020寒假集训专题一搜索E题POJ - 1011
原题链接:http://poj.org/problem?id=1011
专题链接:https://vjudge.net/contest/347799#problem/E
思路
本题看似无从下手,但稍微想想就能明白,答案必须为所有木棍长度总和的约数,这让dfs的做法成为了可能,我们先将小木棍降序排序,从最大的木棍的长度向上枚举即可。
但本题真正难的地方在于剪枝,最重要的一个剪枝在于——在判断了一个木棍不合题意后,其它与其长度相同的木棍肯定也是不符合题意的,因此,我们在将木棍按长度排序后,可以建一个数组n,用于存放与其长度不同的下一个木棍的位置,这样,我们再得知某一个木棍不合题意时可以自己tp到下一个有价值的木棍去,免去了大量的重复工作。
AC代码如下
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include <stack>
using namespace std;
int N,sum;
int arr[67];
int flag[67];//用于标记小木棍是否被用过
int n[67];//用于记录下一个不同长度的木棍的位置
int t;
int co;
bool cam(int a, int b)
{
return a > b;
}
bool dfs(int nowpan, int k, int waitget, int wait)//分别表示现在判断的是否能作为答案的大木棍的长度,现在判断是否要取的小木棍的下标,要凑成大木棍的剩余长度,还剩下几根未取的小木棍
{
if ((waitget == 0) && (wait == 0))//当待取长度和剩余小木棍都为0时合题意
return 1;
if (waitget == 0)
{
waitget =nowpan;
k = 1;
}
for(;k<=co&&k!=0;k++)//这里k!=0用来让使用n数组tp时,最后面几个没有下一个不同长度的木棍能正常结束,而不是在搜索一遍浪费时间(因为这几个元素未被赋值,指向的是0)
{
if (flag[k] != 0)//用过的小木棒肯定不能再用
continue;
if (waitget - arr[k] < 0)//满足此条件说明这个长度的小木棒不能用,直接tp到下一个长度不一样的小木棍那里
{
k = n[k];
}
else
{
flag[k] = 1;
if (dfs(nowpan, k+1, waitget - arr[k], wait-1)==1)
return 1;
flag[k] = 0;//如果能到这里的话,表示取arr【i】不成立,根据此信息进行回溯+剪枝
k = n[k];////能到这里说明这个长度不可用,直接tp到下一个长度不一样的小木棍那里
}
}
return 0;
}
int main()
{
while (cin >> N)
{
memset(arr, 0, sizeof(arr));
if (N == 0)
break;
sum = 0;
co = 0;
for (int i = 1; i <= N; i++)
{
scanf("%d", &t);
if (t <= 50)//用于过滤小木棍长度大于50的
{ ++co;
arr[co] = t;
sum += arr[co];
}
}
sort(arr + 1, arr + co+1,cam);//对木棍进行降序排序
memset(n, 0, sizeof(n));
for (int i = 1;i< co; i++)//用于寻找下一个不同长度的小木棒的位置
{
if (arr[i] != arr[i + 1])
{
t =i+1 ;
for (int j = i; n[j] == 0 && j >=1; j--)
{
n[j] = t;
}
}
}
for (int i = 0;; i++)//从最大小木棍的长度开始枚举
{
if (sum % (arr[1] + i) != 0)//答案必须为所有木棍长度总和的约数
continue;
memset(flag, 0, sizeof(flag));
if (dfs(arr[1] + i,1, arr[1]+i,co))
{
printf("%d\n",arr[1]+i);
break;
}
}
}
return 0;
}