C++ POJ 1011 详解 DFS+花式剪枝 AC代码

mg[66]:小木棍数组
vis[66]:小木棍访问数组
ans:原始木棍长度

注意注释中的用词 @小木棍@原始木棍@还原的长度

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>

using namespace std;


int n;
int mg[66];
bool vis[66];
int sum,ans;
bool ok;

bool cmp(int a,int b) {

	return a>b;
}
//当前已经使用了 kth 根小木棍
//小木棍数组下标 mg_cur 
//当前还原的长度 nowlen
static void dfs(int kth,int mg_cur,int nowlen) { 
	//mg[]  5 5 5 2 2 2 1 1 1
	//vis[] 0 0 0 0 0 0 0 0 0
	if(ok) return;//找到解

	if(kth==n) {//所有小木棍都成功使用了
		//mg[]  5 5 5 2 2 2 1 1 1
		//vis[] 1 1 1 1 1 1 1 1 1
		ok=true;
		return;
	}
	
	if(nowlen==ans) {
		dfs(kth,0,0);//kth不变 下标从0开始 nowlen清零 
		return;
	}
	
	if(nowlen==0) {
		
		int k=0;
		while(vis[k]) k++;//跳过已使用了的小木棍 (解释为什么上面nowlen=ans时 下标从0开始)
		//我们是从最长的小木棍的长度开始枚举 小木棍数组中所有小木棍长度都不会超过ans
		vis[k]=1;//使用第k根小木棍
		dfs(kth+1,k+1,mg[k]);
		vis[k]=0;
		return;
	}

	//nowlen在0和ans之间,说明还没还原完一根原始木棍 还要找

	for(int i=mg_cur; i<n; i++) {
		if(!vis[i]&&nowlen+mg[i]<=ans) {
			//下面是花式剪枝 否则TLE
			//mg[]  5 5 5 2 2 2 1 1 1
			//vis[] 1 0 0 0 0 0 1 0 0
			if(i>0&&!vis[i-1]&&mg[i-1]==mg[i]) continue;
			
			vis[i]=1;//表示使用第i根木棍
			dfs(kth+1,mg_cur+1,nowlen+mg[i]);
			if(ok) return;
			vis[i]=0;
		}

	}
}

int main() {


	while(scanf("%d",&n)&&n) {
		{
			//初始化
			memset(vis,0,sizeof vis);
			ans=0;
			sum=0;
			ok=false;
		}

		//存放小木棍
		for(int i=0; i<n; i++) {
			scanf("%d",&mg[i]);
			sum+=mg[i];//求总和
		}
		sort(mg,mg+n,cmp);//从大到小进行排序
//		for(int i=0;i<n;i++) cout<<mg[i]<<' ';
		for(ans=mg[0]; ans<=sum/2; ans++) { //枚举原木棍长度 不超过总和的一半 不错剪枝
			if(sum%ans) continue;//原始木棍长度一定是总和的因数且不小于最长的小木棍 普通剪枝
			
			dfs(0,0,0);
			if(ok) {
				printf("%d\n",ans);
				break;
			}
		}
		if(!ok) printf("%d\n",sum);
	}



	return 0;
}

原帖在此
https://blog.csdn.net/qq_41280600/article/details/99105347
我加了些个人的理解

发布了7 篇原创文章 · 获赞 15 · 访问量 804

猜你喜欢

转载自blog.csdn.net/weixin_42474371/article/details/99208390