BZOJ3519: [Zjoi2014]消棋子

【题目描述】
消棋子是一个有趣的游戏。游戏在一个r * c的棋盘上进行。棋盘的每个格子,要么是空,要么是一种颜色的棋子。同一种颜色的棋子恰好有两个。每一轮,玩家可以选择一个空格子(x, y),并选择上下左右四个方向中的两个方向,如果在这两个方向上均存在有棋子的格子,而且沿着这两个方向上第一个遇到的棋子颜色相同,那么,我们将这两个棋子拿走,并称之为合法的操作。否则称这个操作不合法,游戏不会处理这个操作。游戏的目的是消除尽量多的棋子。
给出这样一个游戏和一个人的玩法。你需要:
z 指出此人能消去多少棋子。
z 给出一种能消去最多棋子的方案。

【输入格式】
在输入文件eliminate.in中,第一行给出了整数r, c。第二行给出了整数n,表示不同颜色数。接下来n行,第i行含4个整数a[i], b[i], c[i], d[i],表示颜色

为i的两个格子分别是(a[i], b[i]), (c[i], d[i])。然后是一个整数m,表示此人的操作数。接下来m行,每行有2个整数和2个字母,代表了他选择的格子,以及两个方向。我们用“UDLR”分别表示上下左右。

【输出格式】
在输出文件eliminate.out中,第一行输出此人能消去多少棋子。第二行含一个整数k(0 ≤k ≤10^6),表示你给出的方案的操作数。接下来k行,每行2个整数和2个字母,代表你选择的格子以及两个方向。

【样例输入】
4 4
4
1 1 1 4
1 2 3 4
1 3 3 2
4 1 2 3

6
2 3 U R
2 1 D R
2 2 L R
2 4 L D
3 1 L R
3 3 L U

【分析】
其实并不是特别难,也没有传说中的那么烦。
具体思路大概就是开两个Set维护行列相邻的状况,基于STL的二分查找。
第一问直接模拟就行了。
第二问大概也就是模拟= =,先把可以消掉的棋子先消掉,由于这可能影响到所在行所在列的其它棋子,所以我们要维护一个队列,每当有棋子被消掉就把他们加到队列里去。

当时我敲烦起来了,而且机房就要关门了TAT
于是就没有考虑效率直接暴力判断各个方向,具体实现就是在Set里查找当前结点(从队首取出来的)两个方向一共六种的棋子编号是否相同,相同就从Set里删除,入队。

这里是一个小例子和小BUG

讲比较难讲清楚,这里是代码,比较偷懒直接复制粘贴了所以写的有些长= =
然后就是说还有个比较大的BUG……STL_Set不开-O2要超时……感谢BZOJ!

/**************************************************************
    Problem: 3519
    User: Array98
    Language: C++
    Result: Accepted
    Time:5784 ms
    Memory:35740 kb
****************************************************************/

#include <cstdio>
#include <algorithm>
#include <queue>
#include <set>
#define mk(a,b) make_pair(a,b)
using namespace std;
typedef pair<int,int> Pii;
const int MaxN=100010;
set <Pii> Line[MaxN],Row[MaxN];
Pii A1[MaxN],B1[MaxN],C1[MaxN],D1[MaxN];
const Pii NUL=mk(0,0);
int R,C,N,Ans=0;
int Delete[MaxN];
queue <int> Q;
struct Node { int X,Y; char D1,D2; } S[100010];

void Init()
{
    int a,b,c,d;
    scanf("%d%d",&R,&C);
    scanf("%d",&N);
    for (int i=1; i<=N; i++)
    {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        A1[i].first=a;B1[i].first=b;C1[i].first=c;D1[i].first=d;
        A1[i].second= B1[i].second= C1[i].second= D1[i].second=i;
    }
}

void Clear()
{
    for (int i=0; i<=R; i++) { Line[i].clear(); Line[i].insert(mk(0,0)); Line[i].insert((mk(MaxN,0)));}
    for (int i=0; i<=C; i++) { Row[i].clear();  Row[i].insert(mk(0,0));  Row[i].insert((mk(MaxN,0)));}
    for (int i=1; i<=N; i++) 
    {
        Line[A1[i].first].insert(B1[i]);
        Line[C1[i].first].insert(D1[i]);
        Row[B1[i].first].insert(A1[i]);
        Row[D1[i].first].insert(C1[i]);
    }
}

void Solve_ZZY()
{
    Clear();
    int M,X,Y,Ans=0; char D_X,D_Y; Pii Find1,Find2;
    set<Pii>::iterator It;
    scanf("%d",&M);
    for (int i=0; i<M; i++)
    {

        int flag1=0,flag2=0;
        scanf("%d %d %c %c\n",&X,&Y,&D_X,&D_Y);
        Find1=*Line[X].lower_bound(mk(Y,0));
        if (Find1.first==Y) continue;
        Find1=Find2=NUL;
        if (D_X=='U') { Find1=*(--Row[Y].lower_bound(mk(X,0)));   flag1=(Find1.second==0)?0:1;}
        if (D_X=='D') { Find1=*(Row[Y].upper_bound(mk(X,MaxN)));  flag1=(Find1.second==0)?0:1;}
        if (D_X=='L') { Find1=*(--Line[X].lower_bound(mk(Y,0)));  flag1=(Find1.second==0)?0:2;}
        if (D_X=='R') { Find1=*(Line[X].upper_bound(mk(Y,MaxN))); flag1=(Find1.second==0)?0:2;}

        if (D_Y=='U') { Find2=*(--Row[Y].lower_bound(mk(X,0)));   flag2=(Find2.second==0)?0:1;}
        if (D_Y=='D') { Find2=*(Row[Y].upper_bound(mk(X,MaxN)));  flag2=(Find2.second==0)?0:1;}
        if (D_Y=='L') { Find2=*(--Line[X].lower_bound(mk(Y,0)));  flag2=(Find2.second==0)?0:2;}
        if (D_Y=='R') { Find2=*(Line[X].upper_bound(mk(Y,MaxN))); flag2=(Find2.second==0)?0:2;}     

        if (flag1 && flag2 && Find1.second==Find2.second)
        {
            Ans+=1;
            if (flag1==1) { Row[Y].erase(Find1);  Line[Find1.first].erase(mk(Y,Find1.second)); }
            if (flag1==2) { Line[X].erase(Find1); Row[Find1.first].erase(mk(X,Find1.second));  }
            if (flag2==1) { Row[Y].erase(Find2);  Line[Find2.first].erase(mk(Y,Find2.second)); }
            if (flag2==2) { Line[X].erase(Find2); Row[Find2.first].erase(mk(X,Find2.second));  }
        }
    }
    printf("%d ",Ans);  
}

inline void Print(int X, int Y, char D1, char D2) { S[Ans]=(Node){X,Y,D1,D2}; }

inline void Recheck(int X, int Y, int Id)
{
    Pii Find1,Find2;
    Find1=*Line[X].lower_bound(mk(Y,0));
    if (Find1.first==Y) return;

    //UD
    Find1=*(--Row[Y].lower_bound(mk(X,0))); 
    Find2=*(Row[Y].upper_bound(mk(X,MaxN)));
    if (Find1.second && Find1.second==Id && Find1.second==Find2.second  && !Delete[Id])
    {
        Delete[Id]=1; Ans++; Q.push(Id); Print(X,Y,'U','D');
        Row[Y].erase(Find1);  Line[Find1.first].erase(mk(Y,Find1.second));
        Row[Y].erase(Find2);  Line[Find2.first].erase(mk(Y,Find2.second));
        return;
    }

    //LR
    Find1=*(--Line[X].lower_bound(mk(Y,0)));
    Find2=*(Line[X].upper_bound(mk(Y,MaxN)));
    if (Find1.second && Find1.second==Id && Find1.second==Find2.second  && !Delete[Id])
    {
        Delete[Id]=1; Ans++; Q.push(Id); Print(X,Y,'L','R');
        Line[X].erase(Find1); Row[Find1.first].erase(mk(X,Find1.second)); 
        Line[X].erase(Find2); Row[Find2.first].erase(mk(X,Find2.second)); 
        return;
    }

    //UR
    Find1=*(--Row[Y].lower_bound(mk(X,0))); 
    Find2=*(Line[X].upper_bound(mk(Y,MaxN)));
    if (Find1.second && Find1.second==Id && Find1.second==Find2.second  && !Delete[Id])
    {
        Delete[Id]=1; Ans++; Q.push(Id); Print(X,Y,'U','R');
        Row[Y].erase(Find1);  Line[Find1.first].erase(mk(Y,Find1.second));
        Line[X].erase(Find2); Row[Find2.first].erase(mk(X,Find2.second));
        return;
    }

    //UL
    Find1=*(--Row[Y].lower_bound(mk(X,0))); 
    Find2=*(--Line[X].lower_bound(mk(Y,0)));
    if (Find1.second && Find1.second==Id && Find1.second==Find2.second  && !Delete[Id])
    {
        Delete[Id]=1; Ans++; Q.push(Id); Print(X,Y,'U','L');
        Row[Y].erase(Find1);  Line[Find1.first].erase(mk(Y,Find1.second));
        Line[X].erase(Find2); Row[Find2.first].erase(mk(X,Find2.second));
        return;
    }

    //DR
    Find1=*(Row[Y].upper_bound(mk(X,MaxN))); 
    Find2=*(Line[X].upper_bound(mk(Y,MaxN)));
    if (Find1.second && Find1.second==Id && Find1.second==Find2.second  && !Delete[Id])
    {
        Q.push(Id); Delete[Id]=1; Ans++; Print(X,Y,'D','R');
        Row[Y].erase(Find1);  Line[Find1.first].erase(mk(Y,Find1.second));
        Line[X].erase(Find2); Row[Find2.first].erase(mk(X,Find2.second));
        return;
    }

    //DL
    Find1=*(Row[Y].upper_bound(mk(X,MaxN)));
    Find2=*(--Line[X].lower_bound(mk(Y,0))); 
    if (Find1.second && Find1.second==Id && Find1.second==Find2.second  && !Delete[Id])
    {
        Delete[Id]=1; Ans++; Q.push(Id); Print(X,Y,'D','L');
        Row[Y].erase(Find1);  Line[Find1.first].erase(mk(Y,Find1.second)); 
        Line[X].erase(Find2); Row[Find2.first].erase(mk(X,Find2.second));
        return;
    }
}

inline void GotoCheck(int Id)
{
    int X1=A1[Id].first,Y1=B1[Id].first,X2=C1[Id].first,Y2=D1[Id].first;
    if (X1==X2) { Recheck(X1,(Y1+Y2)/2,Id); return; }
    if (Y1==Y2) { Recheck((X1+X2)/2,Y1,Id); return; }
    Recheck(X1,Y2,Id);
    Recheck(X2,Y1,Id);
}

inline void Check(int X, int Y)
{
    Pii Find;   
    Find=*(--Row[Y].lower_bound(mk(X,0)));  if (Find.second) GotoCheck(Find.second);
    Find=*(Row[Y].upper_bound(mk(X,MaxN))); if (Find.second) GotoCheck(Find.second);
    Find=*(--Line[X].lower_bound(mk(Y,0))); if (Find.second) GotoCheck(Find.second); 
    Find=*(Line[X].upper_bound(mk(Y,MaxN)));if (Find.second) GotoCheck(Find.second); 
}

inline void Get_Start()
{
    for (int i=1; i<=N; i++)
    {
        int X1=A1[i].first,Y1=B1[i].first,X2=C1[i].first,Y2=D1[i].first;
        if (X1==X2) { Recheck(X1,(Y1+Y2)/2,i); continue; }
        if (Y1==Y2) { Recheck((X1+X2)/2,Y1,i); continue; }
        Recheck(X1,Y2,i);
        Recheck(X2,Y1,i);
    }
}

void Solve_YPC()
{   
    Clear();
    Get_Start();
    while(!Q.empty())
    {
        int i=Q.front(); Q.pop();
        int X1=A1[i].first,Y1=B1[i].first,X2=C1[i].first,Y2=D1[i].first;
        Check(X1,Y1);
        Check(X2,Y2);
    }
    printf("%d\n",Ans);
    for (int i=1; i<=Ans; i++) printf("%d %d %c %c\n",S[i].X,S[i].Y,S[i].D1,S[i].D2);
}

int main()
{
    Init();
    Solve_ZZY();
    Solve_YPC();
    return 0;
}

谢郭嘉!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值