http://acm.hdu.edu.cn/showproblem.php?pid=1455
题意:几根长度的棍子被分成了很多半。问合成多个长度相同的棍子,棍子长度最小是多少。
题解:很明显是dfs。所以我们首先需要找到,这些棍子可能是多长,肯定是最长的棍子的长度到所有棍子长度和之间的某个长度。找到这些可能之后就直接按照这个长度开始搜。想法是搜到和为这个长度之后记录,然后重新再搜,一直到所有棍子都分配自后就完成了。
重要的剪枝:确定每次搜索的起始位置,这个一定是确定的!!!其次就是相同长度的棍子,一个不符合其他的肯定也不符合!所以要用pre记录前面的不符合的棍子长度!
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int a[200],vis[200],n,flag,ans,sum,ss; int greedy(int max,int sum); void dfs(int max,int pos,int value,int depth); bool compare(int a,int b) { return a>b; } int main(){ freopen("a.txt","r",stdin); while(scanf("%d",&n)!=EOF&&n!=0){ flag=ans=sum=ss=0; memset(a,0,sizeof(a)); memset(vis,0,sizeof(vis)); int max=0; for(int i=0;i<n;i++){ scanf("%d",&a[i]); if(max<a[i]) max=a[i]; sum+=a[i]; } sort(a,a+n,compare);//从大到小排序为了方便搜索,因为很明显棍子越长他的可变性越小,所以先找长的。 printf("%d ",greedy(max,sum)); } return 0; } //找到有多少种棍子长度的可能性 int greedy(int max,int sum){ int k=0,res[1000]; for(int i=max;i<sum;i++){ if(sum%i==0){ res[k]=i; k++; } } for(int i=0;i<k;i++){ memset(vis,0,sizeof(vis)); ss=0; dfs(res[i],0,0,0); if(ss==1) return res[i]; } return sum; } void dfs(int max,int pos,int value,int depth){ if(ss==1){ return; } if(value==max){ depth++; if(depth==sum/max){ ss=1; return; } dfs(max,0,0,depth); return; } if(pos==n){//超过了棍子长度,说明没有可能性,所以直接返回 return; } int pre=-1;//这里pre的用法是为了记录,当前查找的棍子是否符合,不符合的话,就记录下来,在选择其他的时候不和他相同。 for(int i=pos;i<n;i++){ if(!vis[i]&&a[i]+value<=max&&a[i]!=pre){ //首先要保证没有被访问过,其次要判断是否当前的长度加上这个之后小于目标长度,再者就是要求该棍子和前面的不一样。 pre=a[i]; vis[i]=1; dfs(max,i+1,value+a[i],depth); vis[i]=0; if(pos==0){return;} //神器的剪枝,他的神器之处在于,第一次搜索的时候第棍子的情况肯定符合!!确定了每次搜索的起始位置!! } } }