题目:
请继续编写程序,帮助(替代)玩家自动扫雷,用尽可能少的步数最快完成扫雷。请注意, 作为玩家,你的程序并不知道地雷的实际位置。本小题无需编写整个程序,只需实现如下函数:
void machine(int GamePanel[30][30], int n, int m, int &x, int &y);
该函数功能为读入当前游戏界面,并给出决策结果,即在当前游戏界面下,下一步应该点击哪个方格。其参数含义为:数组 GamePanel[][]存储 n 行 m 列整数,表示当前游戏界面,数组中数值的含义同(1)小题。x、y 表示下一步应点击的方格的行号和列号,0 <= x< n,0 <= y< m。也就是说,x 和 y 即本函数的决策结果,表示在当前游戏界面下,应该点击哪个方格。注
意在本题中,machine 函数并不知道后台地雷的实际位置,只能根据当前的游戏界面进行决策。本题不允许自行定义全局变量,且 machine 函数只能读 GamePanel 数组,不允许修改该数组, 即便修改了该数组,下次调用时修改信息也将丢失。
题解:
先开局在一定程度内随机点击格子,然后遍历棋盘,考虑以下几种情况:
①格子已被点开,则概率置1.
②该点该点旁边有雷,统计旁边雷数,并统计旁边未打开的格子数,若雷数等于未打开格子数,则把这些为打开的格子概率置1.
③统计旁边以打开是雷的数和非雷数,若以打开的雷数等于雷数(棋盘中该点旁边的雷数),那么该点旁边的非雷格子一定不是雷。
④统计旁边以打开是雷的数和非雷数,若以打开的雷数小于雷数(棋盘中该点旁边的雷数),那么该点的非雷格子就能计算出概率了。(GamePanel[i][j]-mine)/NoMine.
void machine(int (*GamePanel)[30],int n,int m,int& x,int& y)//根据棋盘判断下一步
{
int i,j;
int sum=0;
// int flag=true;
for(i=0;i<n;i++)//棋局展开程度
{
for(j=0;j<m;j++)
{
if(GamePanel[i][j]!=-1)
{
// flag=false;
// break;
sum++;
}
}
}
if(sum<n*m*0.02)//当棋局展开小于2%时,随机点开
{
x=rand()%n;
y=rand()%m;
return ;
}
double P[30][30];//定义概率数组 ,点击概率最小的那一点 。值越大是雷的概率越大 ,为-1一定不是雷
int MyMine[30][30]; //定义地雷数组 值为0是不确定是否为雷,为1一定是雷,为2一定不是雷
for(i=0;i<n;i++)//初始化概率数组 ,是否为雷和概率都不确定
{
for(j=0;j<20;j++)
{
P[i][j]=0;
MyMine[i][j]=0;
}
}
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(GamePanel[i][j]!=-1)//如果该点已点开,则概率为1,就不会再点了;
{
P[i][j]=1;
}
if(GamePanel[i][j]>0) //该点旁边有雷
{
int sum=0;//定义周围未打开的格子数
for(int p=i-1;p<=i+1;p++)//统计周围未点开的格子数
{
for(int q=j-1;q<=j+1;q++)
{
if(p>=0&&p<n&&q>=0&&q<m)
{
if(GamePanel[p][q]==-1)
{
sum++;
}
}
}
}
if(GamePanel[i][j]==sum)//该点的数刚好为未点开的数,则未点开的全是雷
{
for(int p=i-1;p<=i+1;p++)//把周围未点开的置为雷
{
for(int q=j-1;q<=j+1;q++)
{
if(p>=0&&p<n&&q>=0&&q<m&&GamePanel[p][q]==-1)//GamePanel[p][q]一定为雷
{
MyMine[p][q]=1;//一定为雷
P[p][q]=1;//概率为1,不能点
}
}
}
}
}
}
}
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
double mine=0;
double NoMine=0;
for(int p=i-1;p<=i+1;p++)
{
for(int q=j-1;q<=j+1;q++)
{
if(p>=0&&p<n&&q>=0&&q<m)
{
if(MyMine[p][q]==1)//统计周围的雷数
{
mine++;
}
else if(MyMine[p][q]!=1)//统计周围的非雷数
{
NoMine++;
}
}
}
}
if(GamePanel[i][j]==mine&&NoMine>0)//如果该点数等于周围的雷数,并且周围非雷数大于0
{
for(int p=i-1;p<=i+1;p++)
{
for(int q=j-1;q<=j+1;q++)
{
if(p>=0&&p<n&&q>=0&&q<m&&MyMine[p][q]!=1)//非雷格一定不是雷
{
P[p][q]=-1;
MyMine[p][q]=2;
}
}
}
}
else if(GamePanel[i][j]>mine&&NoMine>0)//如果该点数大于周围的雷数,并且周围非雷数大于0
{
double pro=(GamePanel[i][j]-mine)/NoMine;//求出该点周围非雷格是雷的概率
for(int p=i-1;p<=i+1;p++)
{
for(int q=j-1;q<=j+1;q++)
{
if(p>=0&&p<n&&q>=0&&q<m&&P[p][q]!=-1&&P[p][q]!=1)
{
if(P[p][q]==0)
{
P[p][q]=pro;
}
else if(P[p][q]<pro)
{
P[p][q]=pro;
}
}
}
}
}
}
}
for(i=0;i<n;i++)//找到一定不是雷的格子
{
for(j=0;j<m;j++)
{
if(GamePanel[i][j]==-1&&MyMine[i][j]==2)
{
x=i;
y=j;
return ;
}
}
}
bool fl=false;
int temp;
for(i=0;i<n;i++)//找到第一个未打开的格子。
{
for(j=0;j<m;j++)
{
if(GamePanel[i][j]==-1)
{
temp=P[i][j];
x=i;
y=j;
fl=true;
break;
}
if(fl)
{
break;
}
}
}
for(i=0;i<n;i++)//找出未打开且为雷的概率最小的格
{
for(j=0;j<m;j++)
{
if(P[i][j]<temp&&GamePanel[i][j]==-1)
{
x=i;
y=j;
temp=P[i][j];
}
}
}
return ;
}