2020.11.24 考试题解

搜索专题

T1黑白棋游戏

寻找最少步数,一看就知道是用广搜。 (虽然我考场上用的时深搜+迭代加深)
每次只能与上下左右的交换,如果直接暴力枚举,肯定超时,考虑优化。

<1> 如果有一个点和我们要交换的点颜色一样,就没必要换了。

<2> 我们可以用状压,将每次的判断从(O(16))变为(O(1))

有这些就可以了,要想更进一步,可以用双向搜索(不会,之后有空再补)。

加强版的代码

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define vocaloid(v) (v>='0'&&v<='9')
template <typename T>
il void read(T &x)
{
	x=0;char v=getchar();
	while(!vocaloid(v)) v=getchar();
	while(vocaloid(v)) {x=(x<<1)+(x<<3)+v-'0';v=getchar();}
}
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int a[5][5],b[5][5],vis[(1<<16)-1],fa[(1<<16)-1];
int res[100039][4];
int Beg,End,IA;
struct Ans{
	int x,y,xx,yy,fa;
}ans[100039];
queue<int>q;
il void init()//读入
{
	char c;
	for(int i=1;i<=4;i++)
		for(int j=1;j<=4;j++)
		{
			cin>>c;
			a[i][j]=c-'0';
		}
	for(int i=1;i<=4;i++)
		for(int j=1;j<=4;j++)
		{
			cin>>c;
			b[i][j]=c-'0';
		}
}
il int GetDeci(int x[5][5])//转成压缩的状态
{
	int comb=0,IA=0;
	for(int i=4;i>=1;i--)
		for(int j=4;j>=1;j--)
		{
			comb+=x[i][j]*pow(2,IA);
			IA++;
		}
	return comb;
}
il void change(int x,int a[5][5])//根据状态转回棋盘
{
	while(x)
	{
		for(int i=4;i>=1;i--)
			for(int j=4;j>=1;j--)
			{
				a[i][j]=x%2;
				x/=2;
			}
	}
}
il bool check(int x,int y,int xx,int yy)
{
	if(xx<1||xx>4||yy<1||yy>4||a[x][y]==a[xx][yy]) return 0;
	else return 1;
}
il void bfs()
{
	q.push(Beg);vis[Beg]=1;
	while(!q.empty())
	{
		int x=q.front();q.pop();
		change(x,a);
		for(int i=1;i<=4;i++)
			for(int j=1;j<=4;j++)
			{
				int x=i,y=j;
				for(int k=0;k<=3;k++)
				{
					int xx=x+dx[k],yy=y+dy[k];
					int flag=0;
					if(check(x,y,xx,yy))
					{
						flag=1;
						int now_Deci=GetDeci(a);
						swap(a[x][y],a[xx][yy]);
						int Deci=GetDeci(a);
						if(!vis[Deci])
						{
							vis[Deci]=1;
							ans[Deci]=(Ans){x,y,xx,yy,now_Deci};
							fa[Deci]=now_Deci;
							q.push(Deci);
						}
						if(Deci==End) return ;
					}
					if(flag)
						swap(a[x][y],a[xx][yy]);
				}
			}
	}
}
int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	init();
	Beg=GetDeci(a),End=GetDeci(b);
	bfs();
	fa[Beg]=0;
	while(fa[End])
	{
		IA++;
		res[IA][0]=ans[End].x,res[IA][1]=ans[End].y,res[IA][2]=ans[End].xx,res[IA][3]=ans[End].yy;
		End=fa[End];
	}
	printf("%d
",IA);
	for(int i=IA;i>=1;i--)
		printf("%d%d%d%d
",res[i][0],res[i][1],res[i][2],res[i][3]);
	return 0;
}

T2乌龟棋

对于这道题,我们可以打出一个非常暴力的暴力,时间复杂度大概为(O(m^m)),代码如下:

il void dfs(int pos,int sum)
{
	if(pos==n) 
	{
		ans=max(ans,sum);
		return ;
	}
	for(int i=1;i<=m;i++)
	{
		if(!vis[i])
		{
			vis[i]=1;
			dfs(pos+card[i],sum+a[pos+card[i]]);
			vis[i]=0;
		}
	}
}

肯定超时,所以我们需要优化。

我们注意到题目中说只有(1,2,3,4)四种卡牌,且每张不超过(50)张。所以我们可以开一个四位数组(f_{i,j,p,q}),表示使用了(1)卡牌(i)张,(2)卡牌(j)张,(3)卡牌(p)张,(4)卡牌(q)张时获得的最大分数。

我们不必关心这契卡牌是以怎样的顺序使用的,反正最后都会走到同一个位置,所以我们可以用记忆化搜索或动规,转移方程为:

[f_{i,j,p,q}=max{(f_{i-1,j,p,q},f_{i,j-1,p,q},f_{i,j,p-1,q},f_{i,j,p,q-1})}+a_{now} ]

[now=1+1 imes i+2 imes j+3 imes p+4 imes q ]

接下来就可以直接(dp)了,时间复杂度最大为(O(50^4))
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define vocaloid(v) (v>='0'&&v<='9')
template <typename T>
il void read(T &x)
{
	x=0;char v=getchar();
	while(!vocaloid(v)) v=getchar();
	while(vocaloid(v)) {x=(x<<1)+(x<<3)+v-'0';v=getchar();}
}
int n,m,f[59][59][59][59];
int a[399],g[7];
il int dfs(int i,int j,int p,int q)//记忆化搜索
{
	if(f[i][j][p][q]) return f[i][j][p][q];
	if(i==g[1]&&j==g[2]&&p==g[3]&&q==g[4]) return 0;
	int x=1+1*i+2*j+3*p+4*q;
	int Max=0;
	if(i<g[1]) Max=max(Max,dfs(i+1,j,p,q)+a[x+1]);
	if(j<g[2]) Max=max(Max,dfs(i,j+1,p,q)+a[x+2]);
	if(p<g[3]) Max=max(Max,dfs(i,j,p+1,q)+a[x+3]);
	if(q<g[4]) Max=max(Max,dfs(i,j,p,q+1)+a[x+4]);
	return f[i][j][p][q]=Max;
}
int main()
{
	freopen("tortoise.in","r",stdin);
	freopen("tortoise.out","w",stdout);
	read(n),read(m);int ser;
	for(int i=1;i<=n;i++)	read(a[i]);
	for(int i=1;i<=m;i++)
		read(ser),g[ser]++;
	/*f[0][0][0][0]=a[1];//动态规划
	for(int i=0;i<=g[1];i++)
		for(int j=0;j<=g[2];j++)
			for(int p=0;p<=g[3];p++)
				for(int q=0;q<=g[4];q++)
				{
					if(i==0&&j==0&&p==0&&q==0) continue ;
					int Max=0,x=1+1*i+2*j+3*p+4*q;
					if(i>0) Max=max(Max,f[i-1][j][p][q]+a[x]);
					if(j>0) Max=max(Max,f[i][j-1][p][q]+a[x]);
					if(p>0) Max=max(Max,f[i][j][p-1][q]+a[x]); 
					if(q>0) Max=max(Max,f[i][j][p][q-1]+a[x]);
					f[i][j][p][q]=max(Max,f[i][j][p][q]);
				}
	printf("%d
",f[g[1]][g[2]][g[3]][g[4]]);*/
	printf("%d
",dfs(0,0,0,0)+a[1]);
	return 0;
}
原文地址:https://www.cnblogs.com/MIKU5201314/p/14086713.html