题目:POJ1011.
题目大意:给定
n
n
n根木棍长度为
a
i
a_i
ai,现在要求把它们拼成几根长度相同的木棍(刚好全部用上),使得拼成的木棍单根长度最小.
1
≤
n
≤
64
,
1
≤
a
i
≤
50
1\leq n\leq 64,1\leq a_i\leq 50
1≤n≤64,1≤ai≤50.
这道题貌似是个NP问题,貌似并不能用多项式算法解决,所以考虑大力dfs.
首先我们可以大力枚举最后的长度,范围是 [ 1 , ∑ a i ] [1,\sum a_i] [1,∑ai],然后通过dfs大力判定.
考虑每次dfs都传入当前在填的木棍编号 k k k(前 k − 1 k-1 k−1根木棍已经填完),并且第 k k k根木棍已经填了 s s s的长度.
然后我们就可以通过大力枚举下一次填把哪一根木棍填入来做这道题了,时间复杂度 O ( T L E ) O(TLE) O(TLE).
然后考虑各种玄学优化:
1.先填大的木棍更容易让大量不可行方案在前面被排除掉,所以把
a
i
a_i
ai从大到小排序.
2.很明显最后的单根长度不会小于
a
i
a_i
ai的最大值,所以从
max
{
a
i
}
\max \{a_i\}
max{ai}开始枚举.
3.最后单根长度必然为
∑
a
i
\sum a_i
∑ai的因数.
4.若两种方案中只是木棍的顺序不一样,则这两种方案可以被视为重复,所以拼成的单根木棍中要求长度递减,可以通过在dfs的时候多传一个
l
s
t
lst
lst表示上次枚举到位置.
5.很容易发现若本次加入一根长度为
a
i
a_i
ai的木棍会失败,则换一根长度与
a
i
a_i
ai相等的木棍也会失败,可以不用重复.
6.若当前要填的所有木棍都是空的,那么只要无法填入第一根空的木棍,其它空的木棍也会无法填入.
7.若当前刚好填满一个木棍但接下来会失败,显然用数量更多的木棍填当前木棍的方案也会失败.
具体实现可以看代码.
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=64;
int n,a[N+9],sum,mx;
int len,ans,vis[N+9];
bool cmp(const int &a,const int &b){return a>b;}
bool dfs(int k,int s,int lst){
if (k>sum/len) return 1;
if (s==len) return dfs(k+1,0,1);
int last=0;
for (int i=lst;i<=n;++i){ //优化4,从lst开始枚举
if (vis[i]||s+a[i]>len) continue;
if (a[i]==last) continue; //优化5
vis[i]=1;
if (dfs(k,s+a[i],i+1)) return 1;
last=a[i];
vis[i]=0;
if (s==0||s+a[i]==len) return 0; //优化6&7
}
return 0;
}
Abigail into(){
sum=mx=0;
for (int i=1;i<=n;++i){
scanf("%d",&a[i]);
sum+=a[i];mx=max(mx,a[i]);
}
}
Abigail work(){
sort(a+1,a+1+n,cmp); //优化1
for (len=mx;len<=sum;++len) //优化2
if (sum%len==0){ //优化3
for (int j=1;j<=n;++j) vis[j]=0;
if (dfs(1,0,0)) {ans=len;break;}
}
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
while (~scanf("%d",&n)&&n){
into();
work();
outo();
}
return 0;
}