题目链接
http://bailian.openjudge.cn/practice/1011/
思路一(TLE):
输入数据时,计算出需要枚举的原始棍子长度的上界下界,然后枚举。对每一个stick,将他归到某一组棍子中(如果当前枚举的原始棍子长度为i,一组棍子的总和就应该恰好是i),然后搜索。
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int N, flag;
int stick[68];
int group[64]; // 每一分组暂时的和
bool cmp(int a, int b)
{
return a>b;
}
void f(int index, int g_num, int max_num)
{
// 判断是否超出每一组最大值
int judge = 1;
for(int i=0;i<g_num;i++)
{
if(group[i] != max_num)
{
judge = 0;
break;
}
}
if(judge == 1) // 已经确定这样分组可行
{
flag = 1;
return;
}
// 对元素i,枚举它属于的组数
for(int i=0;i<g_num;i++)
{
if(group[i]+stick[index] <= max_num)
{
group[i] += stick[index];
f(index+1, g_num, max_num);
group[i] -= stick[index];
}
}
}
int main()
{
while(scanf("%d", &N) && N>0)
{
int tot = 0, max_ele = -9999; // 枚举的范围
// 输入stick的值
for(int i=0;i<N;i++)
{
scanf("%d", &stick[i]);
max_ele = max(max_ele, stick[i]);
tot += stick[i];
}
sort(stick, stick+N, cmp);
// 最终应该有 tot/i 组
for(int i=max_ele;i<=tot;i++)
{
flag = 0; // 标记这样分配是否可以
if(tot%i != 0)
continue;
for(int j=0;i<tot/i;j++)
group[j] = 0;
f(0, tot/i, i);
if(flag == 1)
{
printf("%d\n", i);
break;
}
}
}
return 0;
}
思路2:使用当前的棍子,不断拼接成长度为len的原始棍子。(len的值用枚举获得)由大到小排序,因为更长的棍子对其他棍子约束力更大。对于两个长度相同的棍子,如果前一个棍子匹配失败,后一个棍子不用匹配,即可剪枝,加快速度。
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int N, flag;
int stick[68];
int vis[68];
bool cmp(int a, int b)
{
return a>b;
}
void dfs(int tot_used, int now_len, int now_stick, int each_len)
{
if(flag == 1)
return;
if(now_len == 0) // 找到下一个开头的小棒
{
for(int i=0;i<N;i++)
{
if(vis[i] == 0)
{
vis[i] = 1;
dfs(tot_used+1, stick[i], i+1, each_len);
vis[i] = 0;
return;
}
}
}
if(now_len == each_len) // 又拼好一根
{
if(tot_used == N)
{
flag = 1;
return;
}
dfs(tot_used, 0, 0, each_len); // 再重新开始拼接
return;
}
for(int i=now_stick;i<N;i++)
{
if(vis[i]==0 && now_len + stick[i] <= each_len)
{
if(vis[i-1]==0 && stick[i] == stick[i-1])
continue; // 不重复搜索:最重要的剪枝。前一根和他一样的,拼不起来,那它也不用试了
vis[i] = 1;
dfs(tot_used+1, now_len + stick[i], i + 1, each_len);
vis[i] = 0;
}
}
}
int main()
{
while(scanf("%d", &N) && N>0)
{
int tot = 0, max_ele = -9999; // 枚举的范围
// 输入stick的值
for(int i=0;i<N;i++)
{
scanf("%d", &stick[i]);
max_ele = max(max_ele, stick[i]);
tot += stick[i];
vis[i] = 0;
}
sort(stick, stick+N, cmp); // 降序排序
// 最终应该有 tot/i 组
for(int i=max_ele;i<=tot;i++)
{
flag = 0; // 标记这样分配是否可以
if(tot%i != 0)
continue;
//f(0, tot/i, i);
dfs(0, 0, 0, i);
if(flag == 1)
{
printf("%d\n", i);
break;
}
}
}
return 0;
}