chemistry
事先说明:这道题代码,我写的很长,因为最近突然很想练STL,于是各种写,写了一个多小时,调了三个多小时,终于过了样例,于是发上来,庆贺一下(嘻嘻);
题目说明:
一个老师,玩一个很无聊的游戏,一个学生,更无聊地想帮老师玩这个游戏:
给定一个由五个田字格组成的“棋盘”,给定几个棋子,移动他们,要求:每个棋子不能直接走,只能隔着一个棋子跳到下一个位置,且跳完后,被跳的棋子直接挂掉,知道最后只剩一个棋子,游戏结束。输出最少步数,并给出一个步数最少方案。
分析:
首先,输出最少步数,我想,只要稍微有点脑子加眼神较好的同学,在看完这道题后第一反应就是棋子数减一,所以,这里不加细说。
然后,解决其中一个方案,我的思路是深搜:
代码如下:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <iomanip>
#include <ctime>
#include <functional>
#include <cstring>
using namespace std;
int a[8][8];//棋盘位置为(i,j)的地方棋子
int n;
int top;// 指向答案位置的指针
bool k[8][8];
int heap=1;
int zhan2[35];
struct point {int x,y;} p[35];//记录棋子i的坐标
string up="Up";
string down="Down";
string lefts="Left";
string rights="Right";
//---------------------------------------------------
struct for_zhan
{
int x,y;
string t;
for_zhan& operator = (for_zhan& s)
{
x=s.x;
y=s.y;
return *this;
}
for_zhan& operator = (point& s)
{
x=s.x;
y=s.y;
return *this;
}
for_zhan& operator = (string& s)
{
t=s;
return *this;
}
}zhan[35]; //记录答案
ostream& operator << (ostream& out , for_zhan& m)
{
out<<m.x<<' '<<m.y<<"\n"<<m.t<<"\n";
return out;
}
到目前为止只是定义了几个变量,顺便为了方便重定义了一下赋值运算符和输出流,而某些变量的具体作用会在用到的时候加以讲解(哈哈哈,显得自己很厉害,有木有!!)
bool can_up(int x,int y);
bool can_down(int x,int y);
bool can_left(int x,int y);
bool can_right(int x,int y);
//-------------------------------------------
void go_up(int x,int y);
void go_down(int x,int y);
void go_left(int x,int y);
void go_right(int x,int y);
//--------------------------------------------------
void up_go(int x,int y);
void down_go(int x,int y);
void left_go(int x,int y);
void right_go(int x,int y);
额,声明了几个函数看着方便,用着方便,写着方便……(仅仅是因为看着好看而已)(不要问我为什么写了这么多函数,我也不知道,莫名就写了好多,而且都用上了,当然,也可以把他们合到一起)
int main()
{
ios::sync_with_stdio(false);
for(int i=1;i<=7;i++)
{
for(int j=1;j<=7;j++)
{
if( (i<=2 || i>=6) && (j<=2 || j>=6)) k[i][j]=0;
else k[i][j]=1;
}
}
memset(a,0,sizeof(a));
cin>>n;
top=1;
int x,y;
for(int i=1;i<=n;i++)
{
cin>>x>>y;
a[x][y]=i;
p[i].x=x;
p[i].y=y;
}
int ans=n-1;
cout<<ans<<"\n";
dfs(ans);
return 0;
}
主程序(并没有什么用的程序):
首先,k数组用来存放那些点不能走,以防“跳”的时候跳过头(!我!竟!会!忘!)
然后,a[x][y]数组用来存放坐标为(x,y)的位置上的棋子编号为多少(没有就不管它);
再然后,p[i](x,y)代表编号为i的数组所在坐标为(x,y)。
然后各种暴力。
bool can_right(int x,int y)
{
if(a[x][y+1] && !a[x][y+2] && k[x][y+2]) return 1;
return 0;
}
bool can_left(int x,int y)
{
if(a[x][y-1] && !a[x][y-2] && k[x][y-2]) return 1;
return 0;
}
bool can_up(int x,int y)
{
if(a[x-1][y] && !a[x-2][y] && k[x-2][y]) return 1;
return 0;
}
bool can_down(int x,int y)
{
if(a[x+1][y] && !a[x+2][y] && k[x+2][y]) return 1;
return 0;
}
以上这些函数代表能否那样走,包括三个判断:
(1)每个括号里第一个判断代表这个棋子要走的方向上是否有能跳的棋子;
(2)第二个判断代表这个棋子要去的位置上有无棋子;
(3)第三个代表要走的那个位置是否存在(这个我刚开始没想到还有不能走的地方,知道后都要吐血了(汗)。)
void go_down(int x,int y)
{
p[a[x][y]].x+=2;
a[x+2][y]=a[x][y];
zhan2[heap++]=a[x+1][y];
a[x+1][y]=0;
a[x][y]=0;
}
void go_right(int x,int y)
{
p[a[x][y]].y+=2;
a[x][y+2]=a[x][y];
zhan2[heap++]=a[x][y+1];
a[x][y+1]=0;
a[x][y]=0;
}
void go_up(int x,int y)
{
p[a[x][y]].x-=2;
a[x-2][y]=a[x][y];
zhan2[heap++]=a[x-1][y];
a[x-1][y]=0;
a[x][y]=0;
}
void go_left(int x,int y)
{
p[a[x][y]].y-=2;
a[x][y-2]=a[x][y];
zhan2[heap++]=a[x][y-1];
a[x][y-1]=0;
a[x][y]=0;
}
到这里,我错了好几次:
首先,把当前所在位置上的点移到目标位置上(序号的位置(p数组)要移,位置上的序号(a数组)也要移);
然后,把跳过的点(序号)存到zhan2数组里(为了走错路以后能够返回来,具体原理稍后解释);
再把自己原来的位置清空。
void down_go(int x,int y)
{
a[x][y]=a[x+2][y];
a[x+1][y]=zhan2[--heap];
a[x+2][y]=0;
p[a[x][y]].x-=2;
}
void right_go(int x,int y)
{
a[x][y]=a[x][y+2];
a[x][y+1]=zhan2[--heap];
a[x][y+2]=0;
p[a[x][y]].y-=2;
}
void up_go(int x,int y)
{
a[x][y]=a[x-2][y];
a[x-1][y]=zhan2[--heap];
a[x-2][y]=0;
p[a[x][y]].x+=2;
}
void left_go(int x,int y)
{
a[x][y]=a[x][y-2];
a[x][y-1]=zhan2[--heap];
a[x][y-2]=0;
p[a[x][y]].y+=2;
}
void ts() //全名:调试——用于输出所有数。
{
for(int i=1;i<=7;i++)
{
for(int j=1;j<=7;j++)
cout<<a[i][j]<<' ';
cout<<"\n";
}
for(int i=1;i<=n;i++)
cout<<p[i].x<<' '<<p[i].y<<' '<<a[p[i].x][p[i].y]<<"\n";
for(int i=1;i<=heap;i++)
cout<<zhan2[i]<<"\n";
cout<<"\n\n";
}
到这里,我相信应该不会有人看不懂(吧),只是为了代码,额,好看,写了几个函数,为了暴力做铺垫。(不要问我那个ts()是用来干嘛的,写的挺清楚的了,就是用它在各个位置上输出一遍就知道自己哪错了。)
对了,说一下zhan2数组的作用:存储那些点已经挂掉,因为每走一层,都会有一个棋子入栈,这样让棋子“复活”的时候从外往里也是按顺序走的,用一个heap指针指向当前位置,这样就可以让死去的棋子重新富有生机啦!
void dfs(int now)
{
if(!now) //只剩一枚棋子
{
for(int i=1;i<=top;i++)
cout<<zhan[i];
exit(0);
}
for(int i=1;i<=n;i++)
{
if( !a[p[i].x][p[i].y] ) continue; //棋子已被吃
int x=p[i].x;
int y=p[i].y;
if( can_left(x , y) )
{
go_left(x , y);
zhan[top]=p[i];
zhan[top]=lefts;
top++;
dfs(now-1);
left_go(x ,y);
top--;
}
if( can_right(x , y) )
{
go_right(x , y);
zhan[top]=p[i];
zhan[top]=rights;
top++;
dfs(now-1);
right_go(x ,y);
top--;
}
if( can_up(x , y) )
{
go_up(x ,y);
zhan[top]=p[i];
zhan[top]=up;
top++;
dfs(now-1);
up_go(x ,y);
top--;
}
if( can_down(x , y) )
{
go_down(x , y);
zhan[top]=p[i];
zhan[top]=down;
top++;
dfs(now-1);
down_go(x ,y);
top--;
}
}
}
重头戏——暴力出奇迹:
大致思路:能走就走,走不了拉倒,换下一个,都走不了,那就走回来,换点再走,直到死够数了就不走了,直接输出。
大致思路就是这样,但因为用cin,cout输入输出会很慢,于是关闭了流与stdio的同步,这样会很快(比scanf还快!经过测试的!)