蓝桥杯 纵横放火柴游戏 寻找最优解

 
 
    这是一个纵横火柴棒游戏。如图[1.jpg],在3x4的格子中,游戏的双方轮流放置火柴棒。其规则是:
 

   

1. 不能放置在已经放置火柴棒的地方(即只能在空格中放置)。

 
    2. 火柴棒的方向只能是竖直或水平放置。
 
    3. 火柴棒不能与其它格子中的火柴“连通”。所谓连通是指两根火柴棒可以连成一条直线,且中间没有其它不同方向的火柴“阻拦”。
 
    例如:图[1.jpg]所示的局面下,可以在C2位置竖直放置(为了方便描述格子位置,图中左、下都添加了标记),但不能水平放置,因为会与A2连通。同样道理,B2,B3,D2此时两种方向都不可以放置。但如果C2竖直放置后,D2就可以水平放置了,因为不再会与A2连通(受到了C2的阻挡)。
 
    4. 游戏双方轮流放置火柴,不可以弃权,也不可以放多根。直到某一方无法继续放置,则该方为负(输的一方)。
 
    游戏开始时可能已经放置了多根火柴。
 
    你的任务是:编写程序,读入初始状态,计算出对自己最有利的放置方法并输出。
 
    如图[1.jpg]的局面表示为:
 
00-1
-000
0100
 
    即用“0”表示空闲位置,用“1”表示竖直放置,用“-”表示水平放置。
 
【输入、输出格式要求】
   
    用户先输入整数 n(n<100), 表示接下来将输入 n 种初始局面,每种局面占3行(多个局面间没有空白行)。
 
    程序则输出:每种初始局面情况下计算得出的最佳放置法(行号+列号+放置方式)。
 
    例如:用户输入:
2
0111
-000
-000
1111
----
0010
 
   则程序可以输出:
00-
211
 
   不难猜出,输出结果的含义为:
    
   对第一个局面,在第0行第0列水平放置
    
   对第二个局面,在第2行第1列垂直放置
 
   注意:
    
   行号、列号都是从0开始计数的。
        
   对每种局面可能有多个最佳放置方法(解不唯一),只输出一种即可。
 
   例如,对第一个局面,001 也是正解;最第二个局面,201也是正解。
 
 
 
【注意】
 
    请仔细调试!您的程序只有能运行出正确结果的时候才有机会得分!

   


其实,这道题目的核心是到底怎样的一种格局才算是对本方有利的,我们可以这样想:我们的最终目标是让对方无处可放,也就是合法的摆放方案为0,那我们在每一步的目标自然就是让对方可以摆放的方案越少越好。

于是,可以得出结论,存在这么一种摆法,当我方按照这种方式摆放后,对方可以摆放的方案数最少。


分析完了之后自然就要想如何实现,首先,我们需要知道某一种摆放方案是否合法,即在r行c列摆放“-”或“1”到底是不是合法的,我设计了一种判断方法,有点麻烦:


1.判断所要摆放的是“-”还是“1”

2.如果是“-”,就扫描这一行,看看有没有不满足题意的情况,如果是“1”,就扫描这一列,判断有没有不满足条件的情况,因为情况比较少,所以我就把每种情况都拿出来了。


之后我们就开始搜索所有可能的情况,即假如我们放在第一个合法的位置,对手有几种放法,第二个,第三个......然后每次遇到更小的情况后就记录一下我们在哪个位置放了哪种火柴棍,最后输出最小的那种情况。


需要注意的是,这题的解可能有非常多,所以并不是每一组数据都跟题目中的测试数据一样,我们只要保证各个组件没有写错,就可以保证结果的正确性。


#include<iostream>
#include<memory.h>
#include<cmath>
#include<stdio.h>
using namespace std;
char map[3][4];//存储布局
char dir[2]={'-','1'};//存储两种方向
int count,Min,min_r,min_c;//
char stk;
bool legal(char s,int r,int c)//判断在某个位置放上某种棍子是否合法
{
        bool flagr[4];
        bool flagc[3];
        memset(flagr,true,sizeof(flagr));
        memset(flagc,true,sizeof(flagc));
        if(s=='-')
        {
                for(int i=0;i<4;i++)//找出可能不合法的位置
                {
                        if(map[r][i]=='-')
                                flagr[i]=false;
                }

                for(int i=0;i<4;i++)//看看是否真的不合法
                {
                        if(flagr[i]==false)
                        {
                                if(abs(c-i)==1)
                                        return false;
                                if(abs(c-i)==2&&map[r][(c+i)/2]=='0')
                                        return false;
                                if(abs(c-i)==3&&map[r][1]=='0'&&map[r][2]=='0')
                                        return false;


                        }
                }
                return true;
        }
        if(s=='1')
        {
                for(int i=0;i<3;i++)
                {
                        if(map[i][c]=='1')
                                flagc[i]=false;
                        for(int i=0;i<3;i++)
                        {
                                if(flagc[i]==false)
                                {
                                        if(abs(r-i)==1)
                                                return false;
                                        if(abs(r-i)==2&&map[1][c]=='1')
                                                return false;
                                }
                        }
                }
                return true;
        }
}


bool judge()//枚举所有可能的放法
{
        int i,j,k;
        for(i=0;i<3;i++)
        {
                for(j=0;j<4;j++)
                {
                        if(map[i][j]=='0')
                        {
                                for(k=0;k<2;k++)
                                {
                                        if(legal(dir[k],i,j))
                                                count++;
                                }
                        }
                }
        }
}

void search()//寻找使对手选择余地最少的放法
{
        int i,j,k;
        for(i=0;i<3;i++)
        {
                for(j=0;j<4;j++)
                {
                        if(map[i][j]=='0')
                        {
                                for(k=0;k<2;k++)
                                {
                                        if(legal(dir[k],i,j))
                                        {
                                                map[i][j]=dir[k];
                                                judge();
                                               //cout<<count<<endl;
                                                //getchar();
                                                if(Min>count)
                                                {
                                                          Min=count;
                                                          min_r=i;
                                                          min_c=j;
                                                          stk=dir[k];
                                                }

                                                map[i][j]='0';
                                                count=0;
                                        }
                                }
                        }
                }
        }
}


int main()
{
        int cas;
        cin>>cas;
        while(cas--)
        {
                count=0;
                Min=1000;
                min_r=0;
                min_c=0;
                for(int i=0;i<3;i++)
                        cin>>map[i];
                search();
                cout<<min_r<<' '<<min_c<<' '<<stk<<endl;
        }
        return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值