首页 > 代码库 > bzoj 3519: [Zjoi2014] 消棋子 题解

bzoj 3519: [Zjoi2014] 消棋子 题解

【序言】在大家怀疑的眼光下,我做了一个中午和半个下午、调了一个晚上的题目总算A了!

【原题】

消棋子是一个有趣的游戏。游戏在一个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 

1 1 1 4 
1 2 3 4 
1 3 3 2 
4 1 2 3 

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维护相邻的情况,有点像前几天做的那道Garden。对于每一个横行和每一个竖列分别开一个set,表示这一横行和竖列上的坐标。每次的操作都是基于lower_bound的。

首先考虑读入的那个人。显然这个很简单(代码23行),直接模拟即可。比如我们要消掉这样一对棋子。


设左上角(x1,y1),右下角(x2,y2)。我们可以把x1所在的set里的y1删掉,把y1所在的set里的x1删掉,对于x2,y2也如此。每次判断一对点中间路线上是否还有棋子并删除点。

麻烦的还是最优值,主要的麻烦之处在于分类讨论。

先把直接能删的放入队列中,然后每次拿出其中一对点。先删掉这对点,然后判断一下这对点附近是否有哪对点因为删除了这对点而也可以删除了,同时入队。注意一下的一些细节。

①某一点可能会入队好几次,因此要用flag记录一下。

②不要忘记特判x1=x2和y1=y2的情况。

③显然枚举的是(x1,y1)和(x2,y2)四周过去的第一个点。但是注意只是不够的,还有(x1,y2)和(x2,y1)。

④这一点最坑了!注意,lower_bound搜不到的确会返回end(),但是在erase、find等的时候,如果没有特判使带进去的iterator或数值不对,会爆掉的。刚写完后我就一直RE。

然后我就开始漫长的调程序之旅了。已经有点忘却哪里发生了错误,但是反正程序越加越长(虽然行数不是很多)。弄到后来,从对1个点到3个点再到5个点,之后都是大数据了。

于是就开始对拍。为了方便,我只拍最优值的情况。

【造数据程序】

#include<ctime>
#include<cstdlib>
#include<cstdio>
using namespace std;
bool f[40005][40005];int x1,y1,x2,y2;
int main()
{
  freopen("eliminate.in","w",stdout);
  srand((int)time(0));
  int r=10,c=10;
  printf("%d %d\n",r,c);
  int n=5;
  printf("%d\n",n);
  for (int i=1;i<=n;i++)
  {
    while (1)
    {
      x1=rand()%r+1;y1=rand()%c+1;x2=rand()%r+1;y2=rand()%c+1;
      if (!f[x1][y1]&&!f[x2][y2]&&(x1!=x2||y1!=y2)) break;
    }
    printf("%d %d %d %d\n",x1,y1,x2,y2);
    f[x1][y1]=f[x2][y2]=1;
  }
}

真的要好好感谢我的造数据程序。首先是发现了x1==x2特判的时候有点问题。之后就过了8个点,然后剩下的两个最大的点就一直错误,而且答案相差仅一点点!!然后我只好再次无奈的对拍,大约20分钟后拍出了n==5的错误。

一下是错点图片:

                                                       后来才发现,iterator的指针基本每个函数都有,还开了全局变量。这样,Find里的iterator因为执行了一个check过程而有微小的变化。数据真的是太厉害了!

【代码】

#include<cstdio>
#include<cstring>
#include<set>
#include<map>
#define N 100005
using namespace std;
map<int,int>prex[N],prey[N];
multiset<int>P[N],Q[N],X[N],Y[N];
typedef multiset<int>::iterator It;
It it1,it2,it;
int m,n,r,c,h,t,x,y,x1,x2,y1,y2,i,wri[N][2],id[N][4],flag[N];
char re[N][2];
void Person()
{
  scanf("%d",&m);int ans=0,c[2];char s1[2],s2[2];It it[2];
  while (m--)
  {
    scanf("%d%d%s%s",&x,&y,&s1,&s2);
    if (P[x].find(y)!=P[x].end()) continue;
    int o=0,tx,ty;It it[2];c[0]=-1;c[1]=-2;
    if (s1[0]=='U'||s2[0]=='U') {it[o]=Q[y].lower_bound(x);if (it[o]==Q[y].begin()) continue;c[o]=prey[y][*(--it[o])];o++;}
    if (s1[0]=='L'||s2[0]=='L') {it[o]=P[x].lower_bound(y);if (it[o]==P[x].begin()) continue;c[o]=prex[x][*(--it[o])];o++;}
    if (s1[0]=='D'||s2[0]=='D') {it[o]=Q[y].lower_bound(x);if (it[o]==Q[y].end()) continue;c[o]=prey[y][*it[o]];o++;}
    if (s1[0]=='R'||s2[0]=='R') {it[o]=P[x].lower_bound(y);if (it[o]==P[x].end()) continue;c[o]=prex[x][*it[o]];o++;}
    if (c[0]!=c[1]) continue;
    ans+=1;o=0;
    if (s1[0]=='U'||s2[0]=='U') {tx=*it[o];Q[y].erase(it[o]);P[tx].erase(P[tx].find(y));o++;}
    if (s1[0]=='L'||s2[0]=='L') {ty=*it[o];P[x].erase(it[o]);Q[ty].erase(Q[ty].find(x));o++;}
    if (s1[0]=='D'||s2[0]=='D') {tx=*it[o];Q[y].erase(it[o]);P[tx].erase(P[tx].find(y));o++;}
    if (s1[0]=='R'||s2[0]=='R') {ty=*it[o];P[x].erase(it[o]);Q[ty].erase(Q[ty].find(x));o++;}
  }
  printf("%d ",ans);
}
void y1_s_y2(int num)
{
  if (flag[num]) return;
  it1=X[x1].upper_bound(y1);it2=Y[y2].lower_bound(x1);
  if ((*it1>y2||it1==X[x1].end())&&*it2==x2)
    {re[++t][0]='L';re[t][1]='D';wri[t][0]=x1;wri[t][1]=y2;flag[num]=1;return;}
  it1=Y[y1].upper_bound(x1);it2=X[x2].lower_bound(y1);
  if ((*it1>x2||it1==Y[y1].end())&&*it2==y2)
    {re[++t][0]='R';re[t][1]='U';wri[t][0]=x2;wri[t][1]=y1;flag[num]=1;}
}
void y1_b_y2(int num) 
{
  if (flag[num]) return;
  it1=Y[y1].upper_bound(x1);it2=X[x2].upper_bound(y2);
  if ((*it1>x2||it1==Y[y1].end())&&(*it2>y1||it2==X[x2].end()))
    {re[++t][0]='L';re[t][1]='U';wri[t][0]=x2;wri[t][1]=y1;flag[num]=1;return;}
  it1=X[x1].lower_bound(y2);
  it2=Y[y2].lower_bound(x1);
  if (*it1==y1&&*it2==x2)
    {re[++t][0]='R';re[t][1]='D';wri[t][0]=x1;wri[t][1]=y2;flag[num]=1;}
}
void x1_e_x2(int num)
{
  if (flag[num]) return;  
  if (y1>y2) swap(y1,y2);It it=X[x1].upper_bound(y1);if ((*it)<y2) return;
  re[++t][0]='L';re[t][1]='R';wri[t][0]=x1;wri[t][1]=(y1+y2)/2;flag[num]=1;
}
void y1_e_y2(int num)
{
  if (flag[num]) return;
  It it=Y[y1].upper_bound(x1);if ((*it)<x2) return;
  re[++t][0]='U';re[t][1]='D';wri[t][0]=(x1+x2)/2;wri[t][1]=y1;flag[num]=1;
}
void check(int x,int y) 
{
  x1=x;y1=y;int num=prex[x1][y1];
  if (id[num][0]==x1&&id[num][1]==y1) x2=id[num][2],y2=id[num][3];
  else x2=id[num][0],y2=id[num][1];
  if (abs(x1-x2)+abs(y1-y2)==1) return;
  if (x1>x2) swap(x1,x2),swap(y1,y2);int o=0;
  if (x1==x2) {x1_e_x2(num);return;}
  if (y1==y2) y1_e_y2(num);
  if (y1<y2) y1_s_y2(num);
  if (y1>y2) y1_b_y2(num);
}
void DO(int x,int y)
{
    It dd=Y[y].lower_bound(x);int fd=dd==Y[y].end();
    It rr=X[x].lower_bound(y);int fr=rr==X[x].end();
    It uu=dd;int fu=(uu==Y[y].begin());if (!fu) uu--;
    It ll=rr;int fl=(ll==X[x].begin());if (!fl) ll--;
    int d=*dd,r=*rr,u=*uu,l=*ll;
    if (!fd) check(d,y);
    if (!fu) check(u,y);
    if (!fl) check(x,l);
    if (!fr) check(x,r);
}
void Find()
{
  int G1,G2;
  for (int i=1;i<=n;i++)
  {
     x1=id[i][0];
     y1=id[i][1];
     check(x1,y1);
  }
  while (h<t)
  {
    int x=wri[++h][0],y=wri[h][1],tx,ty;
    if (re[h][0]=='U'&&re[h][1]=='D') 
    {
      Y[y].erase(G2=*Y[y].lower_bound(x));
      Y[y].erase(G1=*(--Y[y].lower_bound(x)));
      X[G1].erase(X[G1].find(y));
      X[G2].erase(X[G2].find(y));
      it=Y[y].lower_bound(x);if (it!=Y[y].end()) check(*it,y);if (it!=Y[y].begin()) check(*(--it),y);
      it=X[G1].lower_bound(y);if (it!=X[G1].end()) check(G1,*it);
      if (it!=X[G1].begin()) check(G1,*(--it));
      it=X[G2].lower_bound(y);if (it!=X[G2].end()) check(G2,*it);
      if (it!=X[G2].begin()) check(G2,*(--it));
      continue;
    }
    if (re[h][0]=='L'&&re[h][1]=='R') 
    {
      X[x].erase(G2=*X[x].lower_bound(y));
      X[x].erase(G1=*(--X[x].lower_bound(y)));
      Y[G1].erase(Y[G1].find(x));
      Y[G2].erase(Y[G2].find(x));
      it=X[x].lower_bound(y);if (it!=X[x].end()) check(x,*it);if (it!=X[x].begin()) check(x,*(--it));
      it=Y[G1].lower_bound(x);if (it!=Y[G1].end()) check(*it,G1);
      if (it!=Y[G1].begin()) check(*(--it),G1);
      it=Y[G2].lower_bound(x);
      int t1=*it;It itt=it;int t2=*(--itt);
      if (it!=Y[G2].end()) check(*it,G2);
      if (it!=Y[G2].begin()) check(*(--it),G2);
      continue;
    }
    if (re[h][0]=='U'||re[h][1]=='U') 
      it=--Y[y].lower_bound(x),tx=*it,Y[y].erase(it),X[tx].erase(X[tx].find(y));
    if (re[h][0]=='D'||re[h][1]=='D') 
      it=Y[y].lower_bound(x),tx=*it,Y[y].erase(it),X[tx].erase(X[tx].find(y));
    if (re[h][0]=='L'||re[h][1]=='L') 
      it=--X[x].lower_bound(y),ty=*it,X[x].erase(it),Y[ty].erase(Y[ty].find(x));
    if (re[h][0]=='R'||re[h][1]=='R') 
      it=X[x].lower_bound(y),
      ty=*it,X[x].erase(it),
      Y[ty].erase(Y[ty].find(x));
    DO(x,y);DO(tx,ty);DO(x,ty);DO(tx,y);
  }
  printf("%d\n",t);
}
int main()
{
  freopen("eliminate.in","r",stdin);
  freopen("eliminate.out","w",stdout);
  scanf("%d%d",&r,&c);
  scanf("%d",&n);h=0;t=0;
  for (i=1;i<=n;i++)
  {
    scanf("%d%d",&x,&y);prex[x][y]=prey[y][x]=i;id[i][0]=x;id[i][1]=y;
    P[x].insert(y);Q[y].insert(x);
    scanf("%d%d",&x,&y);prex[x][y]=prey[y][x]=i;id[i][2]=x;id[i][3]=y;
    P[x].insert(y);Q[y].insert(x);
  }
  for (i=1;i<=r;i++) X[i]=P[i];
  for (i=1;i<=c;i++) Y[i]=Q[i];
  Find();
  return 0;
}