FZU ACM 寒假第五讲:搜索算法

1.自然数的拆分问题

Description

任何一个大于 1 的自然数 n,总可以拆分成若干个小于 n 的自然数之和。现在给你一个自然数 n,要求你求出 n 的拆分成一些数字的和。每个拆分后的序列中的数从小到大的顺序,然后你需要输出这些序列,其中字典序小的序列需要优先输出。

思路:

本题要求我们将一个数分成多个数相加的结果,例如数字5可以分为1+4,那么我们可以采用递归来进行深度搜索的方式,枚举从1到不同值的结果

#include<iostream>
#include<cstdio>
using namespace std;
int n,p[11]={1},cnt=1,m;
void print(int aa)
{
	for(int i=1;i<aa;i++)
	{
		cout<<p[i]<<"+";
	}
	cout<<p[aa]<<endl;
}
void dfs(int a)
{
	for(int i=p[a-1];i<=m;i++)
	{
		if(i==n)continue;
		p[a]=i;
		m-=i;
		if(m==0)print(a);
		else dfs(a+1);
		m+=i;
	}
}
int main()
{
	cin>>n;
	m=n;
	dfs(1);
	return 0;
}

2.填涂颜色

Description

由数字 0 组成的方阵中,有一任意形状的由数字 11 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2。例如:6×6 的方阵(n=6),涂色前和涂色后的方阵如下:

如果从某个 0出发,只向上下左右 4 个方向移动且仅经过其他 0 的情况下,无法到达方阵的边界,就认为这个 0 在闭合圈内。闭合圈不一定是环形的,可以是任意形状,但保证闭合圈内的 0 是连通的(两两之间可以相互到达)。

思路:

此题要求闭合圈内的0且不能靠近边界,那么我们可以先将数据读入且另所有1的位置值为1,0的位置赋值为0,为做区分我们将满足条件的0(即最终的答案)定义为3,然后从(1,1)位置开始遍历,通过循环语句和两个控制方向的数组来实现对所有可能位置的遍历,从而判断0是否满足条件,代码如下:

#include<bits/stdc++.h>
using namespace std;
int n;
int kx[5]={0,1,-1,0,0};
int ky[5]={0,0,0,1,-1};
int a[35][35];
void search(int x,int y)
{
	a[x][y]=3;
	for(int i=1;i<=4;i++)
	{
		int x0=x+kx[i];
		int y0=y+ky[i];
		if(x0>0&&x0<=n&&y0>0&&y0<=n&&a[x0][y0]==0)search(x0,y0);
	}
}
int main()
{
	cin>>n;
	char e;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>e;
			if(e=='1')a[i][j]=1;
			else a[i][j]=0;
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(a[i][1]==0)search(i,1);
		if(a[i][n]==0)search(i,n);
	}
	for(int i=1;i<=n;i++)
	{
		if(a[1][i]==0)search(1,i);
        if(a[n][i]==0)search(n,i);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(a[i][j]==0)a[i][j]=2;
			else if(a[i][j]==3)
			{
				a[i][j]=0;
			}
			if(j==1)printf("%d",a[i][j]);
			if(j!=1)printf(" %d",a[i][j]);
		}
		if(i!=n)printf("\n");
	}
	return 0;
}

3.显示图像

Description

古老的显示屏是由 N×M个像素(Pixel)点组成的。一个像素点的位置是根据所在行数和列数决定的。例如 P(2,1) 表示第 2 行第 1 列的像素点。那时候,屏幕只能显示黑与白两种颜色,人们用二进制 0 和 1 来表示。0 表示黑色,1 表示白色。当计算机发出一个指令:P(x,y)=1,则屏幕上的第 x行第 y列的阴极射线管就开始工作,使该像素点显示白色,若 P(x,y)=0,则对应位置的阴极射线管不工作,像素点保持黑色。在某一单位时刻,计算机以 N×M 二维 01 矩阵的方式发出显示整个屏幕图像的命令。

思路:

第二题的简化版,只需要通过循环对所有情况进行遍历即可解题

这道题我们只需要先把所有值为1的点距离设为0(初始化),将其位置依次进队,依次扩展没有计算过距离的点,每次拓展都队头后移,那么队头到1点的距离+1就是拓展的点的距离

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct map
{
	int x,y;
}a[1000001];
bool f[1001][1002];
int d[1010][1010];
int dx[5]={0,0,0,-1,1},dy[5]={0,-1,1,0,0};
int tail=0,head=0;
int main()
{
	memset(f,true,sizeof(f));//初始化为true
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		string s;
		cin>>s;
		for(int j=0;j<s.size();j++)
		{
			if(s[j]=='0')f[i][j+1]=false;//如果是0就标记为没有访问过,1标记为访问过
			else
			{
				d[i][j+1]=0;//初始化距离为0
				f[i][j+1]=true;
            	a[++tail].x=i;
            	a[tail].y=j+1;
			}
		}
	}
	for(head=1;head<=tail;head++)
	{
		for(int i=1;i<=4;i++)
		{
			int xx=a[head].x+dx[i],yy=a[head].y+dy[i];
			if(!f[xx][yy])
			{
				d[xx][yy]=d[a[head].x][a[head].y]+1;
				f[xx][yy]=true;
				a[++tail].x=xx;
				a[tail].y=yy;
			}
		}
	}
	for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        printf("%d ",d[i][j]);
        printf("\n");
    }
    return 0;
}

 4.健康的荷斯坦奶牛 Healthy Holsteins

首先定义一个搜索函数,函数传递的变量为t和s,t表示搜索到了第t种饲料,s表示目前选的饲料的总数。

然后确定搜索的边界,也就是t>饲料的种数,然后判断每次选的那些饲料中的维生素之和是不是都大于等于牛每天需要的每种维他命的最小量(可以用个函数写)。如果是,就判断选的饲料的总数小于以前的最优解(类似于打擂台的那种操作),如果小于,那么当前最优解就要被替换掉,而最优解的一个数组也要被替换掉。最后return即可。

接着开始写函数的主体部分。首先我们选第t种饲料,那么s就要加一,并且我们还要把t存在一个数组当中(想想为什么要存)。然后就可以调用函数,即search(t+1,s+1); 调完函数后,记得要回溯.把t从数组中拿走。接下来我们不选第t种饲料,这样做很简单,直接搜索一步:search(t+1,s); 就行了。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int ans[1000];
int a[1000];
int b[1000][1000];
int c[1000];
int Min=100000;
bool pd(int x)
{
	for(int i=1;i<=n;i++)
	{
		int sum=0;
		for(int j=1;j<=x;j++)sum+=b[c[j]][i];
		if(sum<a[i])return false;
	}
	return true;
}
void search(int t,int s)
{
	if(t>m)
	{
		if(pd(s))
		{
			if(s<Min)
			{
				Min=s;
				for(int i=1;i<=Min;i++)
				{
					ans[i]=c[i];
				}
			}
		}
		return;
	}
	c[s+1]=t;
	search(t+1,s+1);
	c[s+1]=0;
	search(t+1,s);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>b[i][j];
		}
	}
	search(1,0);
	cout<<Min<<" ";
	for(int i=1;i<=Min;i++)
	{
		cout<<ans[i]<<" ";
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值