【序言】在大家怀疑的眼光下,我做了一个中午和半个下午、调了一个晚上的题目总算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

【例子】

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamlhbmdzaGliaWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

【分析】開始感觉思路还是挺简单的,可是代码写起来真的是越写越长。。。

我的基本思路是用set维护相邻的情况,有点像前几天做的那道Garden。对于每个横行和每个竖列分别开一个set,表示这一横行和竖列上的坐标。每次的操作都是基于lower_bound的。

首先考虑读入的那个人。

显然这个非常easy(代码23行),直接模拟就可以。比方我们要消掉这样一对棋子。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamlhbmdzaGliaWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

设左上角(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的错误。

一下是错点图片:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamlhbmdzaGliaWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">                             
                         后来才发现,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] 消棋子 题解的更多相关文章

  1. bzoj 3528 [ZJOI2014] 星系调查 题解

    [原题] 星系调查 [问题描写叙述] 银河历59451年.在银河系有许很多多已被人类殖民的星系.如果想要在行 星系间往来,大家一般使用连接两个行星系的跳跃星门.  一个跳跃星门能够把 物质在它所连接的 ...

  2. BZOJ 4033: [HAOI2015]树上染色题解

    BZOJ 4033: [HAOI2015]树上染色题解(树形dp) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1327400 原题地址: BZOJ 403 ...

  3. BZOJ 1013 | 一份写了一堆注释的高斯消元题解

    题意 给出\(n\)维直角坐标系中\(n + 1\)个点的坐标,它们都在一个\(n\)维球面上,求球心坐标. 题解 设球面上某两个点坐标为\((a_1, a_2, ... a_n)\)和\((b_1, ...

  4. BZOJ 2648: SJY摆棋子 kdtree

    2648: SJY摆棋子 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=2648 Description 这天,SJY显得无聊.在家自己玩 ...

  5. BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流

    传送门 BZOJ 2668 题解 同时分别限制流入和流出次数,所以把一个点拆成三个:入点in(x).中间点mi(x).出点ou(x). 如果一个格子x在初始状态是黑点,则连(S, mi(x), 1, ...

  6. [BZOJ 1032][JSOI 2007]祖玛 题解(区间DP)

    [BZOJ 1032][JSOI 2007]祖玛 Description https://www.lydsy.com/JudgeOnline/problem.php?id=1032 Solution ...

  7. bzoj 1860: [Zjoi2006]Mahjong麻将 题解

    [原题] 1860: [Zjoi2006]Mahjong麻将 Time Limit: 1 Sec  Memory Limit: 64 MB Submit: 211  Solved: 122 [Subm ...

  8. Bzoj 2281 [Sdoi2011]黑白棋 题解

    2281: [Sdoi2011]黑白棋 Time Limit: 3 Sec  Memory Limit: 512 MBSubmit: 592  Solved: 362[Submit][Status][ ...

  9. [Haoi2016]放棋子 题解

    4563: [Haoi2016]放棋子 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 440  Solved: 285[Submit][Status] ...

随机推荐

  1. axure母版(模板)区域介绍

    axure的模板区域是非常重要的一个功能,网站的头部.尾部部分等很多页面同时用到的内容,都可以使用母版,因为在母版中只需要修改一次,就可以实现所有的页面更新,可以大大的加速原型的制作速度.需要重复理解 ...

  2. 对于面向对象的理解(JAVA)

    JAVA 是一门优美的面向对象的语言.那问题就来了什么是对象,这个问题有一个简单的诠释 “万物皆对象”.那么在面向对象编程中对象是类的一个实例,那么什么是类呢?类是一组相似对象的共有特征的抽象.类有什 ...

  3. 改变Emacs下的注释代码方式以支持当前行(未选中情况下)的注释/反注释

    Emacs下支持多行代码的注释/反注释,命令是comment-or-uncomment-region. 我喜欢把它绑定在快捷键C-c C-/上,如下: (global-set-key [?\C-c ? ...

  4. oracle维护表空间和数据文件

    1:重要参考 wiki 2: oracle doc 表空间参考 3:来自dba-oracle的参考 26,27,28,29 一:oracle 表空间概念 表空间是联系数据库的物理磁盘(数据文件)和逻辑 ...

  5. Android-Cannot merge new index 66195 into a non-jumbo instruction的解决的方法

    转载请注明来源:http://blog.csdn.net/goldenfish1919/article/details/33729679 用eclispe打包的时候报错: [2014-06-23 13 ...

  6. 有空研究一下OwnerDraw和三种Windows风格CS_OWNDC和CS_PARENTDC和CS_CLASSDC

    就在StdCtrls.pas, ExtCtrls.pas和Buttons.pas三个类里研究就够了,这是初步的搜索结果: ---------- 在打开的文档中查找 ---------- "G ...

  7. chfn,chsh,last,login,mail ,mesg ,talk,wall,write,nice ,pstree ,renice,skill ,expr ,reset,tset,compress ,lpd ,lpq ,lpr ,lprm,fdformat ,mformat ,mkdosf

    名称:chfn 使用权限:所有使用者 用法:shell>> chfn 说明:提供使用者更改个人资讯,用于finger and mail username 范例: shell>> ...

  8. ActiveX控件的安全初始化和脚本操作 和 数字签名SIGN

    摘要:数字签名SIGN保证控件在下载时候的安全性.如果你的代码已经经过数字签名,即使用户IE的安全设置很高也能下载,安装并登记.但是在页面上初始化,或者用脚本运行这个控件,为了保证安全性,还需要进行M ...

  9. Face Alignment at 3000FPS(C++版)工程配置

    源地址:http://blog.csdn.net/sunshine_in_moon/article/details/49838245/ 3000FPS是人脸对齐算法,特点是速度快!我利用的是think ...

  10. 在VC++中启用内存泄露检测

    检测内存泄漏的主要工具是调试器和 CRT 调试堆函数.若要启用调试堆函数,请在程序中包括以下语句: #define CRTDBG_MAP_ALLOC#include <stdlib.h># ...