0
点赞
收藏
分享

微信扫一扫

第11期:搜索题集(持续更新中......)

yundejia 2022-01-30 阅读 29
算法

1 P1219 [USACO1.5]八皇后 Checker Challenge

#c++搜索与回溯

##基本思路:搜索 标记 AC

###注释:对角线d[i-j]后面必须加上一个n,因为i-j可能为负数,那么数组就会出错,所以将整体向右偏移n个单位(坐标偏移不会影响我们需要达到的目的),将所有可能变成正数;(因为i-j的最小值是-n+1,所以加上一个n就一定会变成一个正数)

本道题最重要的就是记录下皇后占领的格子(打标记的思想),通过此判断下一个皇后是否可以在某个位置,如果可以,则继续搜索下一个皇后可以在的位置,如果不行,则清除标记回到上一步,继续搜索;

可以先考虑六个皇后(即6*6网格),再将6改为n,并且输入n,就可以得出6到13个皇后的解了。

#include<bits/stdc++.h>
using namespace std;
int a[100],b[100],c[100],d[100];
/*
a数组表示的是行;
b数组表示的是列;
c数组表示的是左下到右上的对角线;
d数组表示的是左上到右下的对角线; 
*/ 
int total;//总数:记录解的总数
int n;//输入的数,即N*N的格子,全局变量,搜索中要用
void print(){
	if(total<=2){//保证只输出前三个解,如果解超出三个就不再输出,但后面的total还需要继续叠加 
		for(int k=1;k<=n;k++)
			cout<<a[k]<<" ";//for语句输出
		cout<<endl; 
	}
	total++;//total既是总数,也是前三个排列的判断 
}
void queen(int i){//搜索与回溯主体 
	if(i>n){
		print();
		return;
	}else{
		for(int j=1;j<=n;j++){//尝试可能的位置
			if(!b[j] && !c[i+j] && !d[i-j+n]){//如果没有皇后占领,执行以下程序 
				a[i]=j;//标记i排是第j个
				b[j]=1;//宣布占领纵行
				c[i+j]=1;
				d[i-j+n]=1;
				//宣布占领两条对角线
				queen(i+1);//进一步搜索,下一个皇后
				b[j]=0;
				c[i+j]=0;
				d[i-j+n]=0;
				//(回到上一步)清除标记 
			}
		}
	} 
}
int main(){
	cin>>n;//输入N*N网格,n已在全局中定义
	queen(1);//第一个皇后
	cout<<total<<endl;//输出可能的总数
	return 0; 
}

2 P2392 kkksc03考前临时抱佛脚

枚举每道题交给哪边的脑子解决,找到两边时间较大值的最小值。

对于一道题只有两个状态,一是加到左脑,二是加到右脑,所以是01背包。这里还可以用另一个思想,将一边的脑子加到最接近一半则另一边脑子时间就是正解。

//搜索解法
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x7fffffff;
int Left,Right,minn,ans;
int s[5],a[21][5];
void search(int x,int y){
	if(x>s[y]){
		minn=min(minn,max(Left,Right));
		return;
	}
	Left+=a[x][y];
	search(x+1,y);
	Left-=a[x][y];
	Right+=a[x][y];
	search(x+1,y);
	Right-=a[x][y];//搜索回溯 
}
int main(){
	ios::sync_with_stdio(false);
	for(int i=1;i<=4;i++) cin>>s[i];
	for(int i=1;i<=4;i++){
		Left=Right=0;
		minn=INF;
		for(int j=1;j<=s[i];j++) cin>>a[j][i];
		search(1,i);
		ans+=minn;
	}
	cout<<ans<<"\n";
	return 0;
}
//DP解法
#include<bits/stdc++.h>
using namespace std;
int a[5],sum,t,homework[21],dp[2501];
int main(){
	for(int i=1;i<=4;i++) cin>>a[i];
	for(int i=1;i<=4;i++){
		sum=0;
		memset(dp,0,sizeof(dp)); 
		for(int j=1;j<=a[i];j++){
			cin>>homework[j];
			sum+=homework[j];
		}
		for(int j=1;j<=a[i];j++){
			for(int k=sum/2;k>=homework[j];k--)//只要是总和的一半
				dp[k]=max(dp[k],dp[k-homework[j]]+homework[j]);//01背包 
		}
		t+=sum-dp[sum/2];//累加为另一个脑子 
	}
	cout<<t<<endl;
	return 0;
} 

3 P1443 马的遍历

用queue写bfs

#include<bits/stdc++.h>
using namespace std;
const int dx[8]={-1,-2,-2,-1,1,2,2,1};
const int dy[8]={2,1,-1,-2,2,1,-1,-2};//8个方向
queue<pair<int,int> > q;
int f[500][500];//存步数
bool vis[500][500];//判断走没走过 
int n,m,x,y; 
int main(){
	memset(f,-1,sizeof(f)); memset(vis,false,sizeof(vis));
	cin>>n>>m>>x>>y;
	f[x][y]=0; vis[x][y]=true;
	q.push(make_pair(x,y));
	while(!q.empty()){
		int xx=q.front().first,yy=q.front().second;
		q.pop();//取队首并出队
		for(int i=0;i<8;i++){
			int u=xx+dx[i],v=yy+dy[i];
			if(u<1||u>n||v<1||v>m||vis[u][v]) continue;//出界或走过了就不走
			vis[u][v]=true;
			q.push(make_pair(u,v));
			f[u][v]=f[xx][yy]+1; 
		} 
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			printf("%-5d",f[i][j]);
		}
		printf("\n");
	}
	return 0;
}

4 P1135 奇怪的电梯

这题和上题3 P1443 马的遍历思想一致。

#include<bits/stdc++.h>
using namespace std;
const int N=200+10;
const int dx[2]={1,-1};
queue<int> q; 
int n,a,b,K[N];
int f[N]; //存步数 
bool vis[N]; //判断走过没 
int main(){
	memset(f,-1,sizeof(f)); memset(vis,false,sizeof(vis));
	cin>>n>>a>>b;
	for(int i=1;i<=n;i++) cin>>K[i];
	f[a]=0; vis[a]=true;
	q.push(a);
	while(!q.empty()){
		int xx=q.front();
		q.pop();
		for(int i=0;i<2;i++){
			int u=xx+K[xx]*dx[i];
			if(u<1||u>n||vis[u]) continue;
			vis[u]=true;
			q.push(u);
			f[u]=f[xx]+1;
		}
	}
	cout<<f[b]<<endl;
	return 0;
}

5 P2895 [USACO08FEB]Meteor Shower S

#include<bits/stdc++.h>
using namespace std;
const int dx[5]={0,0,0,1,-1};
const int dy[5]={0,1,-1,0,0};//方便移动和处理陨石砸落 
queue<pair<int,int> > q;//构造队列,存储将处理点x,y坐标 
int n,ma[305][305],v[305][305],sx,sy,st,ans[305][305];
/*n:陨石数量 ma:陨石砸落地图 v:记录是否走过地图 
sx,sy,st:陨石x,y坐标及砸落时间 ans:每个点的最少时间图*/
int read(){
	int x=0,y=1;char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') y=-y;
	for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
	return x*y;
}
void write(int x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int ch(int a){
	if(a==-1) return 99999;
	else return a;
}//判断路过该点时是否陨石已经砸落,如果是没有陨石,相当于n年后砸落 
int main(){
	n=read();
	memset(ma,-1,sizeof(ma));//陨石初始化为-1
	for(int i=1;i<=n;i++){
		sx=read();sy=read();st=read();//输入陨石
		for(int j=0;j<5;j++){//上下左右中标记陨石
			if(sx+dx[j]>=0&&sy+dy[j]>=0&&(ma[sx+dx[j]][sy+dy[j]]==-1||ma[sx+dx[j]][sy+dy[j]]>st)){
			//如果该标记x,y坐标大于0且该点没有被陨石砸落或已标记时间没有该时间早,标记陨石
				ma[sx+dx[j]][sy+dy[j]]=st;	
			} 
		} 
	} 
	v[0][0]=1;//初始点设为已走过
	q.push(make_pair(0,0));//初始点放入队列
	while(!q.empty()){//只要队列不为空
		int x=q.front().first,y=q.front().second;//提取将处理点x,y坐标
		q.pop();
		int s=ans[x][y]+1;//即将标记的点时间是现在点的下一个单位
		if(ma[x][y]==-1){//如果该点安全,输出即将标记的点的时间-1
			write(s-1);
			puts("");
			return 0; 
		} 
		for(int i=1;i<=4;i++){
			int xx=x+dx[i],yy=y+dy[i];//提取将处理点的坐标
			if(xx>=0&&yy>=0&&s<ch(ma[xx][yy])&&v[xx][yy]==0){
			//将处理点需要x,y坐标大于等于0且该点没有走过并且陨石降落时间小于现时间
				q.push(make_pair(xx,yy));//放入将处理队列
				v[xx][yy]=1;//标记已经走过
				ans[xx][yy]=s;//将该点时间放入数组	
			} 
		}
	} 
	puts("-1\n");//如果出不了陨石区,输出-1
	return 0; 
}
举报

相关推荐

0 条评论