洛谷 - P4997 - 不围棋 - 并查集 - 模拟
https://www.luogu.org/problemnew/show/P4997
首先是改变气的定义,使得容易计算,这个很好理解。
然后使用并查集,因为要维护整个连通块的性质。
最后的难点在于,落子把同颜色的连通块连接了,但假如本身就是同一个连通块则不应该计数,所以其实连通块的气不应该手动维护,而应该全权交给并查集去处理。
当然去重之后也是对的。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int INF=0x3f3f3f3f;
int solve();
int main() {
#ifdef Yinku
freopen("Yinku.in","r",stdin);
#endif // Yinku
solve();
}
int n;
char g[605][605];
inline int id(int i,int j) {
return (i-1)*n+j;
}
inline void aid(int f,int &i,int &j) {
j=f%n;
if(j==0)
j=n;
i=(f-j)/n+1;
return;
}
int fa[360005];
//并查集
int qi[360005];
//id对应的位置的气,只有并查集的根保存正确的气
queue<int> Q[2];
int dx[4]= {-1,1,0,0};
int dy[4]= {0,0,-1,1};
char col[2]= {'X','O'};
int find(int x) {
int r=x;
while(fa[r]!=r) {
r=fa[r];
}
while(x!=r) {
int k=fa[x];
fa[x]=r;
x=k;
}
return r;
}
void merge(int x,int y) {
x=find(x);
y=find(y);
if(x==y)
return;
fa[y]=x;
return;
}
void Exit() {
puts("-1 -1");
exit(0);
}
inline void Show() {
cout<<"G:"<<endl;
for(int i=1; i<=n; i++) {
cout<<g[i]+1<<endl;
}
cout<<endl;
/*cout<<"FA:"<<endl;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
printf("%2d ",fa[id(i,j)]);
}
cout<<endl;
}
cout<<endl;*/
cout<<"QI:"<<endl;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
qi[id(i,j)]=qi[find(id(i,j))];
printf("%2d ",qi[id(i,j)]);
}
cout<<endl;
}
cout<<endl;
}
bool RollBackOtherColor(int ox,int oy,int th,int ot) {
int need=0;
for(int k=0; k<4; k++) {
int x=ox+dx[k];
int y=oy+dy[k];
if(x>=1&&x<=n&&y>=1&&y<=n&&g[x][y]==col[ot])
if(qi[find(id(x,y))]<=0){
need=1;
break;
}
}
if(!need)
return 0;
for(int k=0; k<4; k++) {
int x=ox+dx[k];
int y=oy+dy[k];
if(x>=1&&x<=n&&y>=1&&y<=n&&g[x][y]!='.')
qi[find(id(x,y))]++;
}
return 1;
}
bool RollBackThisColor(int ox,int oy,int th,int ot,int Qi) {
int newQi=Qi;
vector<int> visited;
for(int k=0; k<4; k++) {
int x=ox+dx[k];
int y=oy+dy[k];
/*这样同一个连通块的气被重复计算了
if(x>=1&&x<=n&&y>=1&&y<=n&&g[x][y]==col[th])
newQi+=qi[find(id(x,y))];
*/
if(x>=1&&x<=n&&y>=1&&y<=n&&g[x][y]==col[th]){
int r=find(id(x,y));
int s=visited.size();
bool Visited=0;
for(int i=0;i<s;i++){
if(visited[i]==r){
Visited=1;
break;
}
}
if(Visited)
continue;
visited.push_back(r);
newQi+=qi[r];
}
}
/*cout<<"newQi="<<newQi<<endl;
cout<<"Qi="<<Qi<<endl;*/
if(newQi>0) {
//还有气,落子
g[ox][oy]=col[th];
for(int k=0; k<4; k++) {
int x=ox+dx[k];
int y=oy+dy[k];
if(x>=1&&x<=n&&y>=1&&y<=n&&g[x][y]==col[th])
merge(id(ox,oy),id(x,y));
}
qi[find(id(ox,oy))]=newQi;
printf("%d %d\n",ox,oy);
//Show();
return 0;
}
for(int k=0; k<4; k++) {
int x=ox+dx[k];
int y=oy+dy[k];
if(x>=1&&x<=n&&y>=1&&y<=n&&g[x][y]!='.')
qi[find(id(x,y))]++;
}
return 1;
}
int Move(int th,int ot) {
while(1) {
//Show();
//交替下
while(1) {
//重复下同一种棋,防爆栈
if(Q[th].empty()) {
Exit();
}
int f=Q[th].front();
Q[th].pop();
int x,y;
aid(f,x,y);
//cout<<"color="<<col[th]<<" ("<<x<<","<<y<<")"<<endl;
//已经被落子,重来
if(g[x][y]!='.') {
//cout<<"RollBack beacuse there is already a piece"<<endl;
continue;
}
int Qi=0;
for(int k=0; k<4; k++) {
int xx=x+dx[k];
int yy=y+dy[k];
if(xx>=1&&xx<=n&&yy>=1&&yy<=n) {
if(g[xx][yy]!='.')
qi[find(id(xx,yy))]--;
else
Qi++;
}
}
//Show();
if(RollBackOtherColor(x,y,th,ot)) {
//把另一种颜色提子了,回滚重来
//cout<<"RollBack by other color"<<endl;
continue;
}
//那么下这步棋至少不会把另一种棋提子了,接下来看看是不是堵死自己
if(RollBackThisColor(x,y,th,ot,Qi)) {
//把自己提子了,回滚重来
//cout<<"RollBack by this color"<<endl;
continue;
}
//已经被上函数落子
break;
}
//颜色交换
swap(th,ot);
}
return 0;
}
void Init() {
//初始化并查集
for(int i=1; i<=n*n; i++)
fa[i]=i;
//用并查集连通各部分,每个点和他右边、下边的点连接就可以了
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(g[i][j]=='.')
continue;
if(i+1<=n&&g[i][j]==g[i+1][j])
merge(id(i,j),id(i+1,j));
if(j+1<=n&&g[i][j]==g[i][j+1])
merge(id(i,j),id(i,j+1));
}
}
//统计气
memset(qi,0,sizeof(qi));
//每个空位对各个棋子的贡献
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(g[i][j]!='.')
continue;
//插入待使用队列
Q[0].push(id(i,j));
Q[1].push(id(i,j));
for(int k=0; k<4; k++) {
int x=i+dx[k];
int y=j+dy[k];
if(x>=1&&x<=n&&y>=1&&y<=n&&g[x][y]!='.')
qi[find(id(x,y))]++;
}
}
}
return ;
}
int solve() {
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%s",g[i]+1);
}
Init();
Move(0,1);
//先下黑棋
return 0;
}
洛谷 - P4997 - 不围棋 - 并查集 - 模拟的更多相关文章
- bzoj3673 & bzoj3674 & 洛谷P3402 可持久化并查集
题目:bzoj3673:https://www.lydsy.com/JudgeOnline/problem.php?id=3673 bzoj3674:https://www.lydsy.com/Jud ...
- 洛谷 3295 [SCOI2016]萌萌哒——并查集优化连边
题目:https://www.luogu.org/problemnew/show/P3295 当要连的边形如 “一段区间内都是 i 向 i+L 连边” 的时候,用并查集优化连边. 在连边的时候,如果要 ...
- 洛谷P2024 食物链 [NOI2001] 并查集
正解:并查集 解题报告: 传送门(咕了! 其实没有很难(虽然我是交了三发才过的QAQ 但是一来好久没打并查集了恢复一下智力 二来看着智推里唯一一个蓝就很不爽(,,,虽然做了这题之后又补上了个蓝题QAQ ...
- 洛谷P1197 [JSOI2008] 星球大战 [并查集]
题目传送门 星球大战 题目描述 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系. 某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这 ...
- 洛谷 P1551 亲戚(并查集模板)
嗯... 题目链接:https://www.luogu.org/problemnew/show/P1551 思路: 很显然地我们会发现,这是一道并查集的模板题,并且是考察了并查集中的”并“和”查“的操 ...
- 洛谷P1111修复公路并查集改
看了他们的题解感觉很震惊,为什么要用kruskal,这题要用到最小生成树吗??? 38行短短的程序就可以了,我觉得学习不是一种套用,套自己学的,而且题解很大一部分都是kruskal. 个人认为自己的程 ...
- 洛谷P1525关押罪犯——并查集
题目:https://www.luogu.org/problemnew/show/P1525 并查集+贪心,从大到小排序,将二人分在不同房间,找到第一个不满足的即为答案. 代码如下: #include ...
- 洛谷 - P5429 - Fence Planning - 并查集
https://www.luogu.org/problemnew/show/P5429 很明显是要维护整个连通块的共同性质,并查集一搞就完事了. #include<bits/stdc++.h&g ...
- 洛谷 - P1111 - 修复公路 - 并查集
https://www.luogu.org/problemnew/solution/P1111 并查集的水题,水题都错了好多发. 首先并不是有环就退出,而是连通分支为1才退出,每次合并成功连通分支才会 ...
随机推荐
- java中的File文件读写操作
之前有好几次碰到文件操作方面的问题,大都由于时间太赶而没有好好花时间去细致的研究研究.每次都是在百度或者博客或者论坛里面參照着大牛们写的步骤照搬过来,之后再次碰到又忘记了.刚好今天比較清闲.于是就在网 ...
- [网页游戏开发]Morn简介及使用教程
网页游戏开发利器,morn系列教程之Morn简介及使用教程 网页游戏开发的一大部分工作是在和UI制作上,一个好的工具及框架能使开发事半功倍,Adobe自带flash IDE和Flex各有不足. Mor ...
- 自定义 spinner
http://blog.sina.com.cn/s/blog_3e333c4a010151cj.html
- 前端基础——CSS盒子模型
如今很多网页都是由很多个"盒子"拼接.嵌套而成,所以多少接触过网页设计的朋友一定都对CSS盒子模型有所了解. 为了更好的说明,先举个通俗的样例:在一个仓库中放了10个纸箱,每一个纸 ...
- springboot中tomcat找不到jsp页面【转载】
这个原理还没搞明白,只知道是内嵌的tomcat找jsp时默认不读取resources目录,但是具体的默认读取的是哪个目录,打了一下午断点我也没找到.... 修改方式,添加配置修改tomcat的读取目录 ...
- sharding-jdbc源码学习(一)简介
背景 对于大型的互联网应用来说,数据库单表的记录行数可能达到千万级甚至是亿级,并且数据库面临着极高的并发访问.采用Master-Slave复制模式的MySQL架构,只能够对数据库的读进行扩展,而对数据 ...
- GO 入门(一)
1.下载安装go环境 https://golang.org/dl/ 2.检查环境变量配置情况,安装过程中会自动配置:GOROOT 和 Path 3.建立go工作区,并配置 ...
- 超简单易用的 “在 pcduino 开发板上写 Linux 驱动控制板载 LED 的闪烁”
版权声明:本文为博主原创文章,未经博主同意不得转载.转载联系 QQ 30952589,加好友请注明来意. https://blog.csdn.net/sleks/article/details/251 ...
- 简述arp协议的工作原理
在每台安装有TCP/IP协议的电脑里都有一个ARP缓存表,表里的IP地址与MAC地址是一一对应的,如: 我们以主机A(192.168.1.5)向主机B(192.168.1.1)发送数据为例.当发送数据 ...
- 人生苦短之Python函数的健壮性
如何评论一个开发代码写的好?清晰简洁明了?No,No,一个处女座就可以写出来了,整齐地代码,详细的注释不是代码好的标准,应该说不是最重要的标准.代码写的是否健壮才是检验的重要标准. 代码的健壮性: 当 ...