洛谷1120 死在79分 神剪枝+前面搜索状态影响后面

If not me,who? 

错误的思路,

我用了n个小时验证我的思路是有问题的。。。将小木棍从大到小排序后。然后开始从大的木棍开始选取,在选取下一个木棍的时候也是尽量从大的开始选取,但是问题就出现了,这样选取的结果可能不是最优的。。。(就是说会造成不恰当的选取),怎莫说呢,你第一个选取了第i个小木棍,作为初始的小木棍,然后又挑选了一个尽量大的小木棍,作为下一个选取的对象,这个时候两个大的小木棍叠加之后,可能还需要一个很小的木棍就能拼成了。但是我们的原则是尽量留小的木棍,可能本来我们用两个中等长度的小木棍就可以搞定,如此选取就可能会出错。

然后就看了人家的代码,自己看懂了思路之后开始自己写,然后发现tle,就照着大佬的代码改,不知不觉就变成了人家代码的样子,这难道是菜鸟的必经历程?

思路

深搜首先是要找最终结束的标志,这个题目最终结束的标志就是找够sum/len条拼接成功的木棍。深搜的时候,还有一个问题就是当前状态能不能拼接成功,也取决于前面的选择方案。如果前面的选择方案是有问题的,在当前不能拼接成功的情况下,要保证此时return fasle之后,前面的拼接会发生改变。所以有了我下面标注星号的一行代码,前面拼接的情况能够直接的返回true的条件就是后面的情况是可以拼接成功的。(当时我就不知道怎样实现这一点)

for(int i=pre;i<=N;i++){
		if(!vis[i]&&sum+m[i]<=len&&flag!=m[i]){
			vis[i]=1;
********    if(dfs(stick,sum+m[i],i+1)) return true;
		    flag=m[i];
		    vis[i]=0;
			if(sum == 0 || sum+m[i] == len) return false;
		}
}

另外剪枝,真的很重要,还很不好想,特别是下面的第三个剪枝。

剪枝

  1. 排序剪枝
  2. 重复
    当前小木棍不满足条件,那么和它长度相同的小木棍都不满足条件。
  3. 如果当前这个小木棍直接不能拼成,那么就直接return false。这个小木棍如果一个在当前的情况下一个拼接的方案也没有的话,就说明之前的方案是有问题的。所以直接返回

    ***********************不加这个直接tle
if(sum == 0 || sum+m[i] == len) return false;
#include <bits/stdc++.h>
using namespace std;
const int maxn=100;
int m[maxn];
int vis[maxn];
int N,cnt;//cnt记录的是当前的长度可以被分成几部分 
int len;


bool dfs(int stick,int sum,int pre)//从第i个结点开始 
{
	if(stick==cnt+1) return true;
	if(len==sum) return dfs(stick+1,0,1); 
	int flag=-1;//加上一个剪枝 
	for(int i=pre;i<=N;i++){
		if(!vis[i]&&sum+m[i]<=len&&flag!=m[i]){
			vis[i]=1;
			if(dfs(stick,sum+m[i],i+1)) return true;
			flag=m[i];
			vis[i]=0;
			if(sum == 0 || sum+m[i] == len) return false;
		}
	}
	return false;
}

int main()
{
	int temp,sum=0,i,j,k;
	cin >> N;
	for(i=1;i<=N;i++){
		scanf("%d",&temp);
		if(temp>50)
			N--,i--;
		else{
		    m[i]=temp;
		    sum+=temp;
		}
	}
	if(0==N){
		printf("0\n");
		return 0;
	}
	sort(m+1,m+1+N,greater<int>());
	for(len=m[1];len<sum;len++){
		if(0==sum%len){
			cnt=sum/len;
			memset(vis,0,sizeof(vis));
			if(dfs(1,0,1)) break;
		}
	}
	printf("%d",len);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41755258/article/details/84638958