洛谷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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值