传说中的经典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;
}