基础搜索选做

01迷宫

需要一点小小的处理,提前想好再写代码

再就是stl看起来不是特别好用,不过可以避免边界问题啥的。。。

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;

int n,m;
int vis[2001][2001];
char mp[2001][2001];
int ans[2001][2001];

struct dot{
	int x,y;
};
queue<dot>q;

int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};

int v(int x,int y,char val)
{
	if(x>0&&x<=n&&y>0&&y<=n&&val!=mp[x][y]) return 1;
	return 0;
}

void bfs(int x,int y)
{
	while(!q.empty()) q.pop();
	dot tmp1;tmp1.x=x;tmp1.y=y;q.push(tmp1);
	vis[x][y]=1;
	int num=1;
	while(!q.empty())
	{
		dot tmp=q.front();q.pop();
		for(int i=0;i<4;++i)
		{
			int x2=tmp.x+xx[i],y2=tmp.y+yy[i];
			if(v(x2,y2,mp[tmp.x][tmp.y])&&vis[x2][y2]==0)
			{
				dot tmp2;tmp2.x=x2;tmp2.y=y2;
				q.push(tmp2);
				vis[x2][y2]=1;
				++num;
			}
		}
	}
	q.push(tmp1);ans[x][y]=num;
	while(!q.empty())
	{
		dot tmp=q.front();q.pop();
		for(int i=0;i<4;++i)
		{
			int x2=tmp.x+xx[i],y2=tmp.y+yy[i];
			if(v(x2,y2,mp[tmp.x][tmp.y])&&ans[x2][y2]==0)
			{
				dot tmp2;tmp2.x=x2;tmp2.y=y2;
				q.push(tmp2);
				ans[x2][y2]=num;
			}
		}
	}
}


int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			cin>>mp[i][j];
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(vis[i][j]) continue;
			else bfs(i,j);
	for(int i=1;i<=m;++i)
	{
		int x,y;scanf("%d%d",&x,&y);
		printf("%d\n",ans[x][y]);
	}
	return 0;
}

数独

犯了一个很小的错误:在dfs函数判断a[m][n]!=0里面没有加return!导致调了很久不知道错在哪。

还是细节上的问题,下次注意。。。

#include<iostream>
#include<cstdio>

using namespace std;

int a[10][10],x[10][10],y[10][10],z[10][10];
int b[10][10];
int zz[10][10]={{},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};

void f(int m,int n,int val)
{
	a[m][n]=val;
	x[m][val]=1;
	y[n][val]=1;
	z[zz[m][n]][val]=1;
}

void g(int m,int n,int val)
{
	a[m][n]=0;
	x[m][val]=0;
	y[n][val]=0;
	z[zz[m][n]][val]=0;
}

void print()
{
	for(int i=1;i<=9;++i,printf("\n"))
		for(int j=1;j<=9;++j)
			printf("%d ",a[i][j]);
	exit(0);
}

void dfsnxt(int,int);
void dfs(int,int);

int main()
{
//	for(int i=1;i<=9;++i)
//		for(int j=1;j<=9;++j)
//			scanf("%d",&b[i][j]);
	for(int i=1;i<=9;++i)
		for(int j=1;j<=9;++j)
		{
			scanf("%d",&a[i][j]);
			if(a[i][j]==0) continue;
			x[i][a[i][j]]=1;
			y[j][a[i][j]]=1;
			z[zz[i][j]][a[i][j]]=1;
		}
	dfs(1,1);
	return 0;
}

void dfsnxt(int m,int n)
{
	if(n==9&&m==9) print();
	else if(n==9) dfs(m+1,1);
	else dfs(m,n+1);
}

void dfs(int m,int n)
{
	if(a[m][n]) {dfsnxt(m,n);return;}
	for(int i=1;i<=9;++i)
	{
		if(x[m][i]||y[n][i]||z[zz[m][n]][i]) continue;
		f(m,n,i);
		dfsnxt(m,n);
		g(m,n,i);
	}
}

靶型数独

在上一个题数独的基础上加了一点小小的剪枝,就是先遍历0比较少的行,这样可以使dfs的搜索层数最小化。还是要注意细节吧。。。

#include<iostream>
#include<cstdio>

using namespace std;

int ans=-1;
int a[10][10],x[10][10],y[10][10],z[10][10];
int k[10],kk[10],k1[10];

int zz[10][10]={{},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};

void f(int m,int n,int val)
{
	a[m][n]=val;
	x[m][val]=1;
	y[n][val]=1;
	z[zz[m][n]][val]=1;
}

void g(int m,int n,int val)
{
	a[m][n]=0;
	x[m][val]=0;
	y[n][val]=0;
	z[zz[m][n]][val]=0;
}

void print()
{
	int sum=0;
	for(int i=1;i<=9;++i)
		for(int j=1;j<=9;++j)
		{
			int tmp=min(min(i-1,9-i),min(j-1,9-j))+6;
			sum+=tmp*a[i][j];
		}
	ans=max(ans,sum);
}

void dfsnxt(int,int);
void dfs(int,int);

int main()
{
	for(int i=1;i<=9;++i)
	{
		int kkk=0;
		for(int j=1;j<=9;++j)
		{
			scanf("%d",&a[i][j]);
			if(a[i][j]==0) {++kkk;continue;}
			x[i][a[i][j]]=1;
			y[j][a[i][j]]=1;
			z[zz[i][j]][a[i][j]]=1;
		}
		kk[i]=kkk;
	}
	int lst=0;
	for(int i=1;i<=9;++i) {
		int mink=1000,minj;
		for(int j=1;j<=9;++j) {
			if(k[j]==0&&kk[j]<mink) {
				mink=kk[j];
				minj=j;
			}
		}
		k[minj]=i;k1[lst]=minj;lst=minj;
	}
	dfs(k1[0],1);
	printf("%d\n",ans);
	return 0;
}

void dfsnxt(int m,int n)
{
	if(k[m]==9&&n==9) print();
	else if(n==9) dfs(k1[m],1);
	else dfs(m,n+1);
}

void dfs(int m,int n)
{
	if(a[m][n]) {dfsnxt(m,n);return;}
	for(int i=1;i<=9;++i)
	{
		if(x[m][i]||y[n][i]||z[zz[m][n]][i]) continue;
		f(m,n,i);
		dfsnxt(m,n);
		g(m,n,i);
	}
}

小木棍

非常好剪枝,使我感动的旋转

一开始猜错了结论,贪心是错误的,排序并尽量先选最大的并不能使结果一定正确

搜索,有一些经典的和巧妙的剪枝,具体在这里有详细的介绍,我大致列一个提纲

1.记忆化,使用vis数组,不要忘了递归时的还原和整体的清空
2.排序,这个感觉上会降低一点时间
3.设置搜索起点,每次从第i+1个往后搜
4 第一根木棍必须成功,否则直接舍去。这个剪枝适合那种比较离谱的数据
4.1 搜索范围从a[1]开始(亲测从2开始也行,因为剪枝4会直接舍去那些比较小的)
以上这四个都是比较基本的,,,

5.预处理长度相同的木棍,搜索时跳过,适合这种n很小导致很多数据重复的题目。
6.这个剪枝跟上面那个错误的猜想有关,当凑出一部分木棍但继续dfs发现无法完成任务时,立即退出
看起来每个剪枝都不是很起眼,但是少了哪一个都过不了这道题。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int n,a[200],nxt[200],vis[200],sum,now,xx;

bool dfs(int x,int j)
{
	for(int i=j;i<=n;++i)
	{
		if(vis[i]) continue;
		if(x==a[i])
		{
			vis[i]=1;
			--now;
			if(now==0) return 1;
			if(dfs(xx,1)) return 1;
			vis[i]=0;
			++now;
			return 0;
		}
		else if(x>a[i])
		{
			vis[i]=1;
			if(dfs(x-a[i],i+1))
				return 1;
			vis[i]=0;
			i+=nxt[i];
		}
		if(x==xx) return 0;
	}
	return 0;
}

bool cmp(int x,int y) {return x>y;}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) {scanf("%d",&a[i]);sum+=a[i];}
	sort(a+1,a+n+1,cmp);
	for(int i=n-1;i>=1;--i)
		if(a[i]==a[i+1])
			nxt[i]=nxt[i+1]+1;
	if(sum==1) printf("1\n");
	for(int i=2;i<=sum;++i)
	{
		if(sum%i) continue;
		now=sum/i;xx=i;
		if(dfs(i,1))
		{
			for(int j=1;j<=n;++j) vis[j]=0;
			printf("%d\n",i);
			break;
		}
	}
	return 0;
}

走迷宫

题目说的方向:优先左上右下,根据这个对应方向数组

再就是边界、vis数组的起点标记等细节

#include<iostream>
#include<cstdio>

using namespace std;

int n,m,x00,y00,x11,y11;
int a[100][100],vis[100][100];

int xx[4]={0,-1,0,1};
int yy[4]={-1,0,1,0};

struct dot{
	int x,y;
}q[1000];
int cnt,b;

void print(){
	b=1;
	for(int i=1;i<=cnt;++i){
		printf("(%d,%d)",q[i].x,q[i].y);
		if(i==cnt) printf("\n");
		else printf("->"); 
	}
}

bool f(int x,int y){
	if(x>0&&y>0&&x<=n&&y<=m&&vis[x][y]==0&&a[x][y]==1) return 0;
	return 1;
}

void dfs(){
	if(q[cnt].x==x11&&q[cnt].y==y11) {print();return;}
	for(int i=0;i<4;++i){
		int x22=q[cnt].x+xx[i],y22=q[cnt].y+yy[i];
		if(f(x22,y22)) continue;
		q[++cnt].x=x22;q[cnt].y=y22;vis[x22][y22]=1;
		dfs();
		--cnt;vis[x22][y22]=0;
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			scanf("%d",&a[i][j]);
	scanf("%d%d%d%d",&x00,&y00,&x11,&y11);
	q[++cnt].x=x00;q[cnt].y=y00;
	vis[x00][y00]=1;dfs();
	if(!b) printf("-1\n");
	return 0;
}

数的划分

上古题就是很可爱
好像突然发现了去年计概C期中考试的附加题(?)

dfs,对范围进行剪枝和去重,最小可以到n/k上取整,最大是min(n-k+1,z)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;

int n,k;
ll ans;
int vis[1000];

int f(int x,int y){
	if(x%y) return x/y+1;
	return x/y;
}

void dfs(int x,int y,int z){
	if(x==0&&y==0) {++ans;return;}
	int l=f(x,y),r=min(x-y+1,z);
	for(int i=l;i<=r;++i){
		dfs(x-i,y-1,i);
	}
}

int main(){
	scanf("%d%d",&n,&k);
	dfs(n,k,n);
	printf("%lld\n",ans);
	return 0;
}

dp需要一点点技巧(?)就是分含1的和不含1的讨论。跟这道题方程很类似,可惜我考场上没有想到(悲)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n,k,f[1000][1000][2];

int main(){
	scanf("%lld%lld",&n,&k);
	f[1][1][1]=f[2][1][1]=f[2][2][1]=1;
	for(int i=3;i<=n;++i)
		for(int j=1;j<=i;++j){
			f[i][j][0]=f[i-1][j-1][0]+f[i-1][j-1][1];
			f[i][j][1]=f[i-j][j][0]+f[i-j][j][1];
		}
	printf("%lld\n",f[n][k][0]+f[n][k][1]);
	return 0;
}

八数码难题

记忆化bfs,使用map,可以直接赋值,调用的时候未赋值默认返回0,很方便

#include<iostream>
#include<cstdio>
#include<queue>
#include<map>

using namespace std;

struct con{
	int num,a;
};
queue<con>q;
map<int,int>ma;
int ans=123804765;
int xx[4]={-3,-1,1,3};
int x10[10]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};

int f(int x,int d){
	return (x/x10[d])%10;
}

int fnd0(int x){
	int ret=0;
	while(x%10) {x/=10;++ret;}
	return ret;
}

int yes(int d,int x){
	if(d==0) return x==3||x==1;
	if(d==1) return x!=-3;
	if(d==2) return x==3||x==-1;
	if(d==3) return x!=-1;
	if(d==5) return x!=1;
	if(d==6) return x==-3||x==1;
	if(d==7) return x!=3;
	if(d==8) return x==-1||x==-3;
	return 1;
}

int sol(int sta,int d,int x){
	int ret=0,m=1;
	for(int i=0;i<=8;++i){
		if(i==d) ret+=m*f(sta,d+x);
		else if(i==d+x) ret+=m*f(sta,d);
		else ret+=m*f(sta,i);
		m*=10;
	}
	return ret;
}

void bfs(){
	while(!q.empty()){
		con now=q.front();q.pop();
		if(now.a==ans) {
			printf("%d\n",now.num);
			exit(0);
		}
		int d=fnd0(now.a);
		for(int i=0;i<4;++i)
			if(yes(d,xx[i])){
				con tmp;tmp.a=sol(now.a,d,xx[i]);tmp.num=now.num+1;
				if(ma[tmp.a]) continue;
				q.push(tmp);ma[tmp.a]=1;
			}
	}
}

int main(){
	con tmp;scanf("%d",&tmp.a);tmp.num=0;
	ma[tmp.a]=1;q.push(tmp);
	bfs();
	return 0;
}
posted @ 2024-03-15 15:14  hcx1999  阅读(3)  评论(0编辑  收藏  举报