4.10周末总结

这周主要学习了搜索算法的知识

整理了几道比较典型的题目

1.八皇后[USACO1.5]八皇后 Checker Challenge - 洛谷

新生赛的时候就遇到了这道题的变种,不过当时能力欠佳

因为棋盘是正方形,是对称的,方案也是对称的。所以没必要将所有方案搜索出来。

当n为偶数,第一行的棋子放在第[1..⌊n div 2⌋ ]列的方案,与 第一行的棋子放在第[ ⌊n div 2⌋+1 ..n]列的方案 ,数量相等且左右对称。

当 n 为奇数时,第一行的棋子放在第[1..⌊n div 2⌋ ]列的方案,与 第一行的棋子放在第[ ⌈n div 2⌉+1..n]列的方案 ,数量相等且左右对称。第一行的棋子还可以放在第⌈n div 2⌉列,那么,第二行的棋子又具有对称性。

上代码:

#include<bits/stdc++.h>
using namespace std;
const int N=20;
int lg[1<<17];
int n;
int flag;
int sum;
int upperlim;
int a[N];
int cnt;
void print_a(){
    for(int i=1;i<=n;++i)
    printf("%d ",a[i]);
    printf("\n");
}
void dfs(int row,int ld,int rd){
    if(row==upperlim){
        ++sum;
        if(sum<=3)print_a();
        return;
    }
    int pos=upperlim&(~(row|rd|ld));
    if(cnt==0){//要放第一行的棋子
        if(flag==1)pos=(1<<(n>>1))-1;//n为偶数或奇数,第一行的棋子放在第[1..⌊n div 2⌋  ]列。
        else pos=1<<(n>>1);//n为奇数,且第一行的棋子放在第⌈n div 2⌉列
    }
    if(cnt==1&&flag==2)pos=pos&((1<<(n>>1))-1);//n为奇数,且第一行的棋子放在第⌈n div 2⌉列,第二行的棋子放在[1..⌊n div 2⌋-1].
    while(pos){
        register int low=pos&-pos;
        pos^=low;
        a[++cnt]=lg[low];
        dfs(row|low,(ld|low)<<1,(rd|low)>>1);
        --cnt;
    }
}
int main(){
    scanf("%d",&n);
    lg[1]=1;
    for(register int i=0;i<17;++i)
    lg[1<<i]=i+1;
    upperlim=(1<<n)-1;
    flag=1;
    dfs(0,0,0);
    if(n&1){
        flag=2;
        dfs(0,0,0);
    }
    if(n==6)printf("4 1 5 2 6 3\n");//n==6的时候只有四种方案,只好打表了
    printf("%d",sum<<1);
    return 0;    
}

2.选数 [NOIP2002 普及组] 选数 - 洛谷

不断更新后面的数,越靠前的越迟更新

 1后面的数可以是2或3;2后面的数可以是3或4;如此往复


#include<bits/stdc++.h>
using namespace std;
int n,k,he,ans,a[21];

int sushu(int m)//素数判断
{
	for(int i=2;i<m;i++)
	{
		if(m%i==0)
		return 0;
	}
	return 1;
}
void xuanshu(int l,int f)
{
	if(l==0){
	ans+=sushu(he);
	}
	else{
		f++;
	    for(int i=f;i<=n;i++)
	    {
	    	he+=a[i];
	    	l--;
	    	xuanshu(l,i);
	    	he-=a[i];
	    	l++;
		}
	}
	
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	xuanshu(k,0);
	printf("%d",ans);
	return 0;
 }

3.马的遍历马的遍历 - 洛谷

 把棋盘上的每一个点按照规则入队,第一次到达该点时的步数一定是最优步数

#include<bits/stdc++.h>
using namespace std;
struct queue_
{
	int x,y;//一个结构体,x,y是队列该位置放的点的x,y值
} que[160010];
int head=0,tail=1,get[401][401],n,m,sx,sy;
int fx[16]={2,-2,2,-2,-1,1,-1,1},fy[16]={1,1,-1,-1,2,2,-2,-2};//方向
int main()
{
	cin>>n>>m>>sx>>sy;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			get[i][j]=-1;
	get[sx][sy]=0;
	que[1].x=sx;
	que[1].y=sy;
	while(head<tail)
	{
		head++;//头指针加1
		int s=get[que[head].x][que[head].y]+1;
		for(int i=0;i<8;++i)
		{
			int nx=que[head].x+fx[i],ny=que[head].y+fy[i];
			if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&get[nx][ny]==-1)/
			{
				tail++;
				que[tail].x=nx;
				que[tail].y=ny;//新点入队
				get[nx][ny]=s;//标记到达该点的最小步数
			}
		}
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
			cout<<get[i][j];
		cout<<endl;
	}
	return 0; 
}

4.自然数的拆分问题 自然数的拆分问题 - 洛谷

 题目中给了提示,搜取每个节点,如节点没搜到,回溯;

#include<bits/stdc++.h>
using namespace std;
int n;
int a[1000];
void dfs(int x,int h)
{
	int i;
	if(x==0&&h>2)
	{
		cout<<a[n];
		for(i=2;i<h;i++)
		cout<<"+"<<a[i];
		cout<<endl;
		return ;
	}
	for(i=a[h-1];i<=x;i++)
	{
		if(i>=1)
		{
			a[h]=i;
			dfs(x-i,h+1);
		}
	}
}
int main()
{
	cin>>n;
	dfs(n,1);
	return 0;
} 

严格来说,搜索算法也算是一种暴力举策略,但是其算法特性决定了效率比直接的枚举所有答案要高,因为搜索可以跳过一些无效状态,降低问题规模,是一个十分实用的策略。 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值