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

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维护相邻的情况,有点像前几天做的那道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;
}

bzoj 3519: [Zjoi2014] 消棋子 题解,布布扣,bubuko.com

时间: 2024-10-16 01:27:37

bzoj 3519: [Zjoi2014] 消棋子 题解的相关文章

bzoj 3528 [ZJOI2014] 星系调查 题解

[原题] 星系调查 [问题描述] 银河历59451年,在银河系有许许多多已被人类殖民的星系.如果想要在行 星系间往来,大家一般使用连接两个行星系的跳跃星门.  一个跳跃星门可以把 物质在它所连接的两个行星系中互相传送. 露露.花花和萱萱被银河系星际联盟调查局任命调查商业巨擘ZeusLeague+ 的不正当商业行为. 在银河系有N个已被ZeusLeague+成功打入市场的行星系,不妨标号为 1,2,...,N.而ZeusLeague+在这N个行星系之间还拥有自己的M个跳跃星门.使 用这些跳跃星门,

bzoj 2419 &amp; poj 3532 电阻 题解

[原题] 2419: 电阻 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 131  Solved: 51 Description 你突破了无数艰难险阻,终于解决了上面那道题,众神犇瞬间就 震惊了.他们发现居然有人可以把那种非人类做的题目做出来. 他们一致同意,最后这道题不能再出数学题了.考虑到两位小盆 友的状态,他们决定考考你的初中物理水平. 一个电路板,有 N 个接点,M 个电阻.电阻两端都在接点上, 都告诉了你阻值.询问1 号点与N 号点的

bzoj 2109 &amp; 2535 航空管制 题解

[] [分析]真的是一道贪心好题.开始我以为是一道大水题.建立拓扑图后(没环就是方便!),直接把最外层设定序号为1,第二层为2,bfs下去即可...结果发现:飞行序号不能相同...于是开始想. 先考虑第一个问题:打印一个合法序列.我开始是这么想的: 观察每个飞机的最晚飞行序号Ki,因为必定有解,所以我们可以让它的序号就是Ki.然后用它的时间去更新前面的时间(图可以反向建立).应该可以维护一个大根堆,每次挑出最大的一个进行处理. [简易代码] memset(T,0x7f,sizeof(T)); f

bzoj 1858: [Scoi2010] 序列操作 题解

[原题] 1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 1031  Solved: 529 [Submit][Status] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内

矩阵乘法专题3——bzoj 1898 [Zjoi2004]Swamp 沼泽鳄鱼 题解

[原题] 1898: [Zjoi2004]Swamp 沼泽鳄鱼 Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 425  Solved: 256 [Submit][Status] Description 潘塔纳尔沼泽地号称世界上最大的一块湿地,它地位于巴西中部马托格罗索州的南部地区.每当雨季来临,这里碧波荡漾.生机盎然,引来不少游客.为了让游玩更有情趣,人们在池塘的中央建设了几座石墩和石桥,每座石桥连接着两座石墩,且每两座石墩之间至多只有一座石桥.这

矩阵乘法专题4——bzoj 2326 [HNOI2011] 数学作业 题解

转载请注明:http://blog.csdn.net/jiangshibiao/article/details/24963747 [原题] 2326: [HNOI2011]数学作业 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 853  Solved: 473 [Submit][Status] Description [分析]我们按数字的位数来划分.对于K位数,我们就可以专门设计一个矩阵来计算. 然后就是注意细节了. [代码] #include

bzoj 1874 取石子游戏 题解 &amp; SG函数初探

[原题] 1874: [BeiJing2009 WinterCamp]取石子游戏 Time Limit: 5 Sec  Memory Limit: 162 MB Submit: 334  Solved: 122 [Submit][Status] Description 小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子. In

bzoj 2802: [Poi2012]Warehouse Store 题解

[原题] 2802: [Poi2012]Warehouse Store Time Limit: 10 Sec  Memory Limit: 64 MBSec  Special Judge Submit: 94  Solved: 54 Description 有一家专卖一种商品的店,考虑连续的n天. 第i天上午会进货Ai件商品,中午的时候会有顾客需要购买Bi件商品,可以选择满足顾客的要求,或是无视掉他. 如果要满足顾客的需求,就必须要有足够的库存.问最多能够满足多少个顾客的需求. Input 第一

斜率优化专题5——bzoj 1096 [ZJOI2007]仓库建设 题解

[原题] 1096: [ZJOI2007]仓库建设 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 1998  Solved: 816 [Submit][Status] Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚. 由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用.突然有一天,L公司的总裁L先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是L先生决