本题是经典DFS+剪枝题。
题目链接:http://acm.hit.edu.cn/hoj/problem/view?id=1049
但是裸DFS我都写了一段时间,必然是TLE的,但是是一种很好的搜索思路,贴裸DFS代码:
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;
int array[10000];
int used[10000];
bool cmp(int a,int b)
{
return a>b;
}
int sum;
int len;
int piece_num;
int n;
//cur-当前拼接长度,poi-当前棍索引
//fin-已完成的原棍数
bool dfs(int cur,int poi,int fin)
{
if(cur == len)
{
fin++;
cur = 0;
poi = 1;
}
if(fin == piece_num)
{
return true;
}
for(int i=poi;i<n;i++)
{
if(used[i] == 0)
{
if(cur + array[i]<=len)
{
used[i] = 1;
if(dfs(cur+array[i],i+1,fin))
{
return true;
}
used[i] = 0;
}
}
}
return false;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int max;
while(scanf("%d",&n)!=EOF && n!=0)
{
sum = 0;
for(int i=0;i<n;i++)
{
scanf(" %d",&array[i]);
sum += array[i];
}
sort(array,array+n,cmp);
memset(used,0,sizeof(used));
max = array[0];
for(len = max;len<=sum;len++)
{
if(sum%len == 0)
{
piece_num = sum/len;
if(dfs(0,0,0))
{
printf("%d\n",len);
break;
}
}
}
}
return 0;
}
然后加剪枝优化搜索,共加了四个剪枝,但是前两个剪枝已经足够将时间压到0S。AC掉
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;
int array[64];
int used[64];
int next[64];
bool cmp(int a,int b)
{
return a>b;
}
int sum;
int len;
int piece_num;
int n;
int tail;
//指向下一个不同与此棍长度的索引
void countNext()
{
memset(next,-1,sizeof(next));
int i = 0;
int c = 0;
while(i<n)
{
c = 0;
for(int j=i+1; j<n; j++)
{
if(array[i]!=array[j])
{
c = j;
break;
}
}
if(c == 0)
{
c = n;
}
for(int j=i; j<c; j++)
{
next[j] = c;
}
i = c;
}
}
//求出刚好能足够拼出一个木棍原型长度的标号
void countTail()
{
int s = 0;
for(int i=n-1;i>=n;i++)
{
s += array[i];
if(s>=len)
{
break;
}
}
tail = s;
}
int countRes(int m)
{
//计算下标大于等于m且未使用的小棒总长之和
int sumr=0;
for (int i=m;i<n;i++)
{
if (used[i]==false)
{
sumr += array[i];
}
}
return sumr;
}
//cur-当前拼接长度,poi-当前棍索引
//fin-已完成的原棍数
bool dfs(int cur,int poi,int fin)
{
if(cur == len)
{
fin++;
cur = 0;
poi = 1;
}
if(fin == piece_num)
{
return true;
}
int i = poi;
while(i<n)
{
if(used[i] == 0)
{
if(cur + array[i]<=len)
{
used[i] = 1;
if(dfs(cur+array[i],i+1,fin))
{
return true;
}
used[i] = 0;
//剪枝2:如果当前cur = 0不成立,那么此len无效,不用再验证以后
if(cur == 0)
{
return false;
}
//剪枝3:若尝试一根小棒恰能组合出当前原棒的剩余长度的小棒失败,则不再尝试比它更小的小棒
if(cur+array[i] == len)
{
return false;
}
}
//剪枝1:长度相同的棍只测验一次
i = next[i];
//剪枝4:计算当前所剩可用小棒是否足以拼出当前这支木棒原型
if(i>=tail && countRes(i)<len - cur)
{
return false;
}
continue;
}
i++;
}
return false;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int max;
while(scanf("%d",&n)!=EOF && n!=0)
{
sum = 0;
for(int i=0; i<n; i++)
{
scanf(" %d",&array[i]);
sum += array[i];
}
sort(array,array+n,cmp);
max = array[0];
countNext();
for(len = max; len<=sum; len++)
{
if(sum%len == 0)
{
memset(used,0,sizeof(used));
countTail();
piece_num = sum/len;
if(dfs(0,0,0))
{
printf("%d\n",len);
break;
}
}
}
}
return 0;
}