POJ 2706 Connect BFS + 判断线段相交

题意:在n*n的棋盘上有依次放上m个棋子。黑子先手。判断最后一个黑子所连成的线段能否贯穿其区域。

若两颗子中间已有直线存在(端点除外),则此两子不能连。且在最后一个黑子之前,不存在黑子连线贯穿整个区域的情况。

判断两线段相交:

因为此题不存在水平线段或者数值线段,所以可以丧心病狂的使用斜率。

设t1,t2为等待被连线的两个点,该线段所在直线斜率为k1,截距为b;

设t3,t4为有可能阻断t1,t2连线的两个点,其所在的斜率亦为k1的直线的截距为b2.b3;

若b,b2,b3满足 (b2  < b < b3)  ||  (b2  >  b > b3) 则说明t3,t4所在直线与t1,t2所在直线相交。

然此题是线段,所以还需加限制条件。

如下图:

只需在符合两直线相交的情况下判断两点均在黑线所围成的区域能即可。

剩下的就是简单的BFS了,不再赘述。


#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <algorithm>

using namespace std;

struct C
{
    int x,y;
    C *next;
}*head[25][25];

struct P
{
    int x,y;
};

C *creat()
{
    C *p = (C *)malloc(sizeof(C));
    p->next = NULL;
    return p;
}

int mc[25][25];

int jx[] = {-2,-2,-1, 1, 2, 2, 1,-1};//八个方向
int jy[] = {-1, 1, 2, 2, 1,-1,-2,-2};

bool checkC(int x1,int y1,int x2,int y2)//检查与(x1,y1)相连的点中有木有(x2,y2);
{
    for(C *p = head[x1][y1]->next;p != NULL; p = p->next)
    {
        if(p->x == x2 && p->y == y2)
            return true;
    }
    return false;
}

bool judgeinc(int minx,int maxx,int miny,int maxy,int x,int y,int n)//检查(x,y)是否在黑线所围成的区域内
{
    int minx1, maxx1, miny1, maxy1;
    minx1 = minx-1 >= 0 ? minx-1 : 0;
    maxx1 = maxx+1 <= n ? maxx+1 : n;
    miny1 = miny-1 >= 0 ? miny-1 : 0;
    maxy1 = maxy+1 <= n ? maxy+1 : n;

    int i,j;

    for(i = minx1;i <= maxx1; ++i)
    {
        for(j = miny;j <= maxy; ++j)
        {
            if(i == x && j == y)
                return true;
        }
    }


    for(i = minx;i <= maxx; ++i)
    {
        for(j = miny1;j <= maxy1; ++j)
        {
            if(i == x && j == y)
                return true;
        }
    }

    return false;


}

bool judgeC(P t1,P t2,int n)//判断两点之间是否有线段阻隔
{
    int minx,miny,maxx,maxy;

    P temp;

    minx = (t1.x < t2.x ? t1.x : t2.x);
    maxx = (t1.x > t2.x ? t1.x : t2.x);
    miny = (t1.y < t2.y ? t1.y : t2.y);
    maxy = (t1.y > t2.y ? t1.y : t2.y);

    double k,b,b1,b2;
    int i,j,u;

    k = (t2.y-t1.y)*1.0/(t2.x-t1.x);
    b = t2.y*1.0 - k*t2.x;

    //实际上只需要检查与t1,t2在同一 ‘日’ 字内的其余四个点
    
    for(i = minx;i <= maxx; ++i)
    {
        for(j = miny;j <= maxy; ++j)
        {
            if( !( (i == t1.x && j == t1.y) || (i == t2.x && j == t2.y) ) )
            {
                b1 = j*1.0 - k*i;

                for(u = 0;u < 8;++u)
                {
                    temp.x = i+jx[u];
                    temp.y = j+jy[u];

                    if(0 <= temp.x && temp.y <= n && 0 <= temp.y && temp.y <= n)
                    {
                        b2 = temp.y*1.0 - k*temp.x;
                        if( ( (b1 > b && b > b2) || (b1 < b && b < b2) ) && judgeinc(minx,maxx,miny,maxy,temp.x,temp.y,n) && checkC(i,j,temp.x,temp.y) )
                            return false;
                    }
                }
            }
        }
    }
    return true;
}

void link(P t1,P t2)//将t2插入t1的链表内  说明二者可连接
{
    C *p = head[t1.x][t1.y];
    C *q = creat();
    q->x = t2.x;
    q->y = t2.y;
    q->next = p->next;
    p->next = q;
}

void connect(int x,int y,int type,int n)
{
    int i;

    P t1,t2;
    t1.x = x;
    t1.y = y;

    for(i = 0;i < 8; ++i)
    {
        t2.x = t1.x+jx[i];
        t2.y = t1.y+jy[i];

        if(0 <= t2.x && t2.x <= n && 0 <= t2.y && t2.y <= n && mc[t2.x][t2.y] == type && judgeC(t1,t2,n))
        {
            link(t1,t2);
            link(t2,t1);
        }
    }
}

bool MarkVisit[25][25];

bool judge(int x,int y,int n)//判断最后是否胜利
{
    queue<P> q;

    memset(MarkVisit,false,sizeof(MarkVisit));

    P t1,t2;
    t1.x = x;
    t1.y = y;
    q.push(t1);
    MarkVisit[x][y] = true;

    bool m1 = false,m2 = false;

    C *p;

    while(q.empty() == false && (m1 == false || m2 == false))
    {
        t1 = q.front();
        q.pop();

        if(t1.y == 0)
            m1 = true;
        if(t1.y == n)
            m2 = true;

        for(p = head[t1.x][t1.y]->next; p != NULL ; p = p->next)
        {
            t2.x = p->x;
            t2.y = p->y;
            if(MarkVisit[t2.x][t2.y] == false)
            {
                q.push(t2);
                MarkVisit[t2.x][t2.y] = true;
            }
        }
    }

    if(m1 && m2)
        return true;
    return false;
}

int main()
{
    int n,m;
    int i,j;
    int x,y;
    int step;

    n = 22;

    for(i = 0; i <= n; ++i)
    {
        for(j = 0; j <= n; ++j)
        {
            head[i][j] = creat();
        }
    }

    while(scanf("%d %d",&n,&m) && (n||m))
    {
        for(i = 0; i <= n; ++i)
        {
            for(j = 0; j <= n; ++j)
            {
                mc[i][j] = 0;
                head[i][j]->next = NULL;
            }
        }

        for(step = 1; step <= m; ++step)
        {
            scanf("%d %d",&y,&x);
            if(step&1)
            {
                mc[x][y] = 1;
                connect(x,y,1,n);
            }
            else
            {
                mc[x][y] = 2;
                connect(x,y,2,n);
            }
        }


        if(judge(x,y,n))
            cout<<"yes"<<endl;
        else
            cout<<"no" <<endl;

    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值