191031-搜索剪枝专题

191031-搜索剪枝专题

几种剪枝方法

1、可行性剪枝

所谓可行性剪枝,顾名思义,就是当当前状态和题意不符,并且由于题目可以推出,往后的所有情况和题意都不符,那么就可以进行剪枝,直接把这种情况及后续的所有情况判负,直接返回。

即:不可行,就返回。

2、排除等效冗余

所谓排除等效冗余,就是当几个枝桠具有完全相同的效果的时候,只选择其中一个走就可以了。

即:都可以,选一个。

3、最优性剪枝

所谓最优性剪枝,是在我们用搜索方法解决最优化问题的时候的一种常用剪枝。就是当你搜到一半的时候,已经比已经搜到的最优解要不优了,那么这个方案肯定是不行的,即刻停止搜索,进行回溯。

即:有比较,选最优。

4、顺序剪枝

普遍来讲,搜索的顺序是不固定的,对一个问题来讲,算法可以进入搜索树的任意的一个子节点。但假如我们要搜索一个最小值,而非要从最大值存在的那个节点开搜,就可能存在搜索到最后才出解。而我们从最小的节点开搜很可能马上就出解。这就是顺序剪枝的一个应用。一般来讲,有单调性存在的搜索问题可以和贪心思想结合,进行顺序剪枝。

即:有顺序,按题意。

5、记忆化

记忆化搜索其实是搜索的另外一个分支。在这里简单介绍一下记忆化的原理:

就是记录搜索的每一个状态,当重复搜索到相同的状态的时候直接返回。

即:搜重了,直接跳。

T1 小猫爬山

题目描述

Freda和rainbow只好花钱让它们坐索道下山。索道上的缆车最大承重量为W,而N只小猫的重量分别是C1、C2……CN。当然,每辆缆车上的小猫的重量之和不能超过W。每租用一辆缆车,Freda和rainbow就要付1美元,所以他们想知道,最少需要付多少美元才能把这N只小猫都运送下山?

解析

搜索剪枝
1,最优化剪枝,如果当前的缆车数量已经超过ans,那么直接return
2,顺序剪枝,按小猫重量大小降序排序,以此来快速去掉无效分支

题解

#include<bits/stdc++.h>
using namespace std;
bool comp(const int &a,const int &b)
{
	return a>b;
}
int n,w,a[10001],ans,used[10001];
void dfs(int cur,int num)
{
	if(num>=ans)
		return;
	if(cur==n+1)
	{
		ans=min(ans,num);
		return;
	}
	for(int i=1;i<=num;i++)
		if(used[i]+a[cur]<=w)
		{
			used[i]+=a[cur];
			dfs(cur+1,num);
			used[i]-=a[cur];
		}
	int cnt=num+1;
	used[cnt]+=a[cur];
	dfs(cur+1,cnt);
	used[cnt]-=a[cur];
}
int main()
{
	scanf("%d%d",&n,&w);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	sort(a+1,a+1+n,comp);
	ans=1e8;
	dfs(1,1);
	printf("%d",ans);
	return 0;
}

T2 Sudoku Poj2676

解析

搜索(细节比较多,需要注意)

题解

#include  <cmath>
#include  <cstdio>
#include  <climits>
#include  <cstring>
#include  <cstdlib>
#include  <iostream>
#include  <algorithm>
using namespace std;
const int fx[10]={0,0,0,0,1,1,1,2,2,2};
const int fy[10]={0,1,1,1,2,2,2,3,3,3};
int f[11][11],bel[11][11],T;
bool row[11][11],col[11][11],squ[11][11],bj;
char ch[11][11],s;
void clear()
{
	memset(row,0,sizeof(row));
	memset(col,0,sizeof(col));
	memset(bel,0,sizeof(bel));
	memset(squ,0,sizeof(squ));
	memset(f,0,sizeof(f));
	bj=0;
}
void out()
{
	for(int i=1;i<=9;i++)
	{
		for(int j=1;j<=9;j++)
			printf("%d",f[i][j]);
		printf("\n");
	}
}
void dfs(int x,int y)
{
	if(bj) return;
	if(f[x][y])
	{
		if(x==9&&y==9) 
		{
			out();
			bj=1;
			return;
		}
		else if(y==9) dfs(x+1,1);
		else dfs(x,y+1);//学习如何,从一个状态到下一个状态
	}
	else
	{
		for(int i=1;i<=9;i++)
			if(!squ[bel[x][y]][i]&&!row[y][i]&&!col[x][i])
			{
				f[x][y]=i;
				squ[bel[x][y]][i]=row[y][i]=col[x][i]=true;
				if(x==9&&y==9) 
				{
					out();
					bj=1;
					return;
				}
				else if(y==9) dfs(x+1,1);
				else dfs(x,y+1);
				squ[bel[x][y]][i]=row[y][i]=col[x][i]=false;
				f[x][y]=0;
			}
			
	}
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		clear();
		for(int i=1;i<=9;i++)
		{
			scanf("%c",&s);
			for(int j=1;j<=9;j++)
			{
				scanf("%c",&ch[i][j]);
				f[i][j]=ch[i][j]-'0';
				bel[i][j]=3*fx[i]+fy[j];//每个点所属的块
				row[j][f[i][j]]=1;//打标记
				col[i][f[i][j]]=1;
				squ[bel[i][j]][f[i][j]]=1;
			}
		}
		dfs(1,1);
	}
	return 0;
}

题外话

该题还可以继续优化,比如可以先搜索限制比较多的点,这样可以快速去掉无用分支
如16*16数独例题,传送门 (poj3076)

T3 彩票

解析

搜索剪枝
1,可行性剪枝1,如果当前得到的和加上剩余可选的最大的和小于 x y \frac{x}{y} yx,直接return
2,可行性剪枝2,如果当前得到的和加上剩余可选的最小的和大于 x y \frac{x}{y} yx,直接return

题解

#include<bits/stdc++.h>
using namespace std;
int x,y,n,m,maxn;
double ans,sum[100];
void dfs(int cur,int num,double dx)
{
	if(num==n)
	{
		if(fabs(dx-ans)<0.000000001)//注意精度
			maxn++;
		return;
	}
	if(dx+sum[n-num+cur-1]-sum[cur-1]<ans-0.000000001)
		return;
	if(dx+sum[m]-sum[m-n+num]>ans)
		return;
	if(cur>m)
		return;
	dfs(cur+1,num+1,dx+(1.0/cur));
	dfs(cur+1,num,dx);
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&x,&y);
	for(int i=1;i<=m;i++)
		sum[i]=sum[i-1]+(1.0/i);
	ans=1.0*x/y;
	dfs(1,0,0);
	printf("%d",maxn);
	return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值