每日一宏
#define 大法师 dfs
大法师万岁
题目
题目描述
原题来自:CERC 1995
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入格式
第一行为一个单独的整数 N 表示砍过以后的小木棍的总数。 第二行为 N 个用空格隔开的正整数,表示 N 根小木棍的长度。
输出格式
输出仅一行,表示要求的原始木棍的最小可能长度。
样例
输入
9
5 2 1 5 2 1 5 2 1
输出
6
代码详解
#include<bits/stdc++.h>
using namespace std;
int id[65];
vector<int> vec;
int n,ans=-0x3f3f3f,len;
int gs,sum;
int cnt;
bool vis[65];
bool f;
struct cmp{//functor
bool operator () (const int &a,const int &b){
return a>b;
}
};
void dfs(int,int,int);
int main()
{
cin>>n;
vec.push_back(0x3f3f3f);//先放进去一个特别大的数,便于后边下标从1开始
for(int i=1;i<=n;i++){
int tmp;
cin>>tmp;
if(tmp<=50){//一定要判断是否在50以内
ans=max(ans,tmp);//找出最大值
sum+=tmp;
vec.push_back(tmp);//从后面放入tmp
}
}
sort(vec.begin(),vec.end(),cmp());
cnt=vec.size()-1;
id[cnt]=cnt;
for(int i=cnt;i>=1;i--){
if(vec[i]==vec[i-1]){
id[i-1]=id[i];
}
else{
id[i-1]=i-1;
}
}
for(int i=ans;i<=sum/2;i++){
if(sum%i==0){//原始木棍的长度肯定是sum的因数
f=0;
len=i;
vis[1]=1;//默认一定选第一根
gs=sum/i;//根数
dfs(1,1,len-vec[1]);
if(f){
cout<<i;
return 0;
}
}
}
cout<<sum;
return 0;
}
void dfs(int k,int last,int rest){//k为当前分到第几根木棍,last为上一次分到哪个木棍,rest是凑完这根木棍还需要的长度
int i;
if(!rest){//如果rest到0,那么这根木棍找完了
if(k==gs){//如果k到了总根数,则找到答案
f=1;
return;
}
for(i=1;i<=cnt;i++){//找到一个没标记的木棍
if(!vis[i]){
vis[i]=1;
break;
}
}
dfs(k+1,i,len-vec[i]);//找下一根木棍
vis[i]=0;//回溯
if(f){//有答案就return
return;
}
}
for(i=last+1;i<=cnt;i++){//顺着上一次的继续找
if(vec[i]<=rest&&!vis[i]){//找rest能减去的
break;
}
}
for(;i<=cnt;i++){
if(!vis[i]){
vis[i]=1;
dfs(k,i,rest-vec[i]);
vis[i]=0;//回溯
if(f||rest==vec[i]){//大剪枝
return;
}
}
i=id[i];//大剪枝
}
}
一眼大法师
首先,我们要遍历原始小木棍的长度
范围是从最长的小木棍的长度开始,一直到sum/2(想一想,为什么)
遍历到sum/2后,再往后除了sum本身就没有sum的因数了,所以只需要遍历到sum/2
如果到sum/2还没有答案
那就输出sum(一段也不分)
接下来又到了快乐的剪枝环节
根据题意得出以下剪枝方案:
剪枝①
id[cnt]=cnt;
for(int i=cnt;i>=1;i--){
if(vec[i]==vec[i-1]){
id[i-1]=id[i];
}
else{
id[i-1]=i-1;
}
}
id这个数组的作用是指向连续相等的数中最后一个的下标
比如说我已经将样例排好序了,id的值如下表
| 值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| vec | 5 | 5 | 5 | 2 | 2 | 2 | 1 | 1 | 1 |
| id | 3 | 3 | 3 | 6 | 6 | 6 | 9 | 9 | 9 |
因为同一长度的木棍效果是相同的,
所以一个不行,后面的都不行,所以要跳过
i=id[i];
剪枝②
if(rest==vec[i]){
return;
}
如果说此时rest等于vec[i],
就不必再搜了(先前已经搜了一遍,因为rest减去vec[i]等于0,所以进行了一次判断,开始下一个木棍的搜索),直接return掉
优化①
struct cmp{
bool operator () (const int &a,const int &b){
return a>b;
}
};
此代码名为functor,是sort排序速度唯一能超过快排的方法
是快排速度的一倍多,所以特别推荐
优化②
vector<int> vec;
vec.push_back(0x3f3f3f);
vec.push_back(tmp);
sort(vec.begin(),vec.end(),cmp());
cnt=vec.size()-1;
vector浅显易懂
不仅随便从前从后进数
而且可以随便从前从后删数
还是无限大的数组
稍微一定义就可以当数组使用
具体用法可以看这位大佬的
小结
一本通上是可以ac的
但洛谷94分过不了不知道为啥
欢迎大佬暴打本蒟蒻
文章介绍了如何使用深度优先搜索(DFS)解决一个编程问题,即根据给定的小木棍长度,找出原始木棍的最小可能长度。通过遍历可能的木棍长度和利用剪枝策略优化搜索过程,最终找到满足条件的解。代码中还包含了排序优化和数据结构的使用。

3711

被折叠的 条评论
为什么被折叠?



