题意:George把一些等长的树枝切成了n个长度不超过50的短树枝,现在给你这些短树枝的长度,计算出原来树枝的最小长度。
思路:DFS,搜索的时候记录下每个树枝是否被匹配过,搜索的数据包括当前已经匹配的长度、目标长度、已经匹配的树枝数。剪枝很重要:
1、这些树枝的原长度一定大于短树枝的最大长度,同时也是小树枝总长度的因子,这个作为初步剪枝。
2、把树枝从大到小排序,开始匹配一根新的树枝的时候,如果第一根就不符合结果,那么后面的结果一定也不符合,很重要的一步剪枝,决定了代码从TLE到0msAC。
3、排序后的树枝,如果当前树枝不满足,就跳过后面所有等长的树枝。
#include<bits/stdc++.h>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
int a[70],n,f;
bool vis[70];
bool cmp(int a,int b){
return a>b;
}
void dfs(int pos,int len,int g,int num){//当前下标、当前长度、目标长度、已匹配数量
//printf("dfs: %d %d %d %d\n",pos,len,g,num);
if(f)return;
if(num==n){//所有树枝都匹配上了,说明有解
f=1;
return;
}
for(int i=pos;i<n;++i){
if(!vis[i]&&a[i]+len<=g){
vis[i]=1;
if(a[i]+len==g){
dfs(0,0,g,num+1);
}else{
dfs(i+1,a[i]+len,g,num+1);
}
vis[i]=0;//去除标记
if(f)return;//找到了结果,返回
if(len==0)return;//剪枝2,很关键!
while(i<n&&a[i+1]==a[i])++i;//剪枝3
}
}
}
int main(){
int sum;
while(scanf("%d",&n),n){
memset(vis,0,sizeof(vis));
sum=0;
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
sum+=a[i];
}
sort(a,a+n,cmp);//从大到小排序,搜索的时候优先选择大的,减少冲突的情况
for(int i=a[0];i<=sum;++i){
if(i==sum)printf("%d\n",sum);//剪枝
else if(sum%i==0){//剪枝1
f=0;
dfs(0,0,i,0);
if(f==1){
printf("%d\n",i);
break;
}
}
}
}
}
最初的代码还是各种奇葩错误,强大的逻辑和优秀的剪枝很关键,以后应该更多练习!