HDU 5925 Coconuts
2016 CCPC 东北四省赛 D.
一道好题.
现场写崩了.
赛后LSh跟我讲了一种离散化的做法, 没听懂.
题意
一个$R \cdot C\ (R, C\le 10^9)$ 的矩形点阵上有 $n \ (n \le 200) $ 个坏点, 其余都是好点.
求好点的4-连通块 (以下简称cell) 的个数和每块的大小.
做法
我的想法是:
找出一个坏点的8-连通块, 在框住这个块的(最小)矩形区域内, 搜索当前8-连通块以及整个矩形点阵的边界 (以下简称"fence") 包围的cell.
这个想法大体上没什么漏洞.
要注意的问题有
- fence 套 fence 的情况
- dfs的各种边界情况的顺序 (我是用dfs搜索好点的4-连通块的)
对于fence 套 fence 的情况, 如果对两个fence不加区分 就会导致重复统计.
Solution: 对每个 (可能的) fence 上的坏点编号.
实现上的坑
这题的坑真多 (至少对蒟蒻我而言如此)
一开始核心代码是这样写的:
int dx[]{-1, 1, 0, 0}, dy[]{0, 0, 1, -1};
bool used[205][205];
int dfs(int _x, int _y){
if(_x==0 || _x==r+1 || _y==0 || _y==c+1){
return -2;
}
for(int i=0; i<n; i++){
if(_x==x[i] && _y == y[i]){
return vis[i]==id?0:-3;
}
}
// good nut
if(_x<x1 || _x > x2 || _y < Y1 || _y > y2){
return -1;
}
if(used[_x-x1][_y-Y1]){
return 0;
}
used[_x-x1][_y-Y1]=true;
int res=0;
bool flag=false;
for(int i=0; i<4; i++){
int nx=_x+dx[i], ny=_y+dy[i];
int tmp = dfs(nx, ny);
if(tmp == -1){
return -1;
}
else if(tmp>=0) flag=true;
else if(tmp>0) res+=tmp;
}
return flag?res+1:0;
}
这个dfs的目的是搜索好点的4-连通块, 同时判断该连通块是否合法.
然而我这种用返回值判断合法性的dfs写法却隐藏着一个bug:
注意dfs中的这样一段代码:
for(int i=0; i<4; i++){
int nx=_x+dx[i], ny=_y+dy[i];
int tmp = dfs(nx, ny);
if(tmp == -1){
return -1;
}
else if(tmp>=0) flag=true;
else if(tmp>0) res+=tmp;
}
其中的
if(tmp == -1){
return -1;
}
当dfs到一个超出当前矩形框的点时, 就会返回-1.
这样的写法在某些情况下会让好点也成为fence的一部分, 从而无法搜出一个好点的4-连通块. (这一点还会详谈)
比较好的写法:
vis数组+全局变量
一个好点的连通块合法的条件:
- 不超出矩形框
- 至少能遇到一个当前fence上的坏点
我们用两个全局的bool值 (flag) 来表示这两个条件是否成立, 再用一个全局变量记录当前连通块的大小.
Implementation
#include <bits/stdc++.h>
using namespace std;
const int N=205;
int x[N], y[N];
int vis[N];
// vector<int> st;
int r, c, n;
int x1, x2, Y1, y2;
int id;
void dfs(int i){
// st.push_back(i);
vis[i]=id;
x1=min(x1, x[i]);
x2=max(x2, x[i]);
Y1=min(Y1, y[i]);
y2=max(y2, y[i]);
for(int dx=-1; dx<=1; dx++)
for(int dy=-1; dy<=1; dy++){
int nx=x[i]+dx, ny=y[i]+dy;
for(int j=0; j<n; j++){
if(!vis[j] && x[j]==nx && y[j]==ny){
dfs(j);
break; // ?
}
}
}
}
int dx[]{-1, 1, 0, 0}, dy[]{0, 0, 1, -1};
bool used[205][205];
bool f1, f2;
int cnt;
void dfs(int _x, int _y){
// on border
if(_x==0 || _x==r+1 || _y==0 || _y==c+1){
return;
}
// out of range
if(_x<x1 || _x > x2 || _y < Y1 || _y > y2){
f1=false;
return;
}
// bad nut
for(int i=0; i<n; i++){
if(_x==x[i] && _y == y[i]){
if(vis[i]==id)
f2=true;
return;
}
}
// good nut
if(used[_x-x1][_y-Y1]){
return;
}
// cout<<_x<<' '<<_y<<endl;
used[_x-x1][_y-Y1]=true;
++cnt;
for(int i=0; i<4; i++){
int nx=_x+dx[i], ny=_y+dy[i];
dfs(nx, ny);
}
}
vector<long long> res;
long long tot;
void clac(){
for(int i=x1; i<=x2; i++)
for(int j=Y1; j<=y2; j++){
used[i-x1][j-Y1]=false;
}
for(int i=x1; i<=x2; i++)
for(int j=Y1; j<=y2; j++){
f1=true, f2=false;
cnt=0;
dfs(i, j);
// cout<<cnt<<endl;
// if(f1) puts("f1");
// if(f2) puts("f2");
if(f1 && f2 && cnt>0){
tot-=cnt;
res.push_back(cnt);
}
}
}
void solve(int n){
res.clear();
for(int i=0; i<n; i++){
vis[i]=0;
}
tot=(long long)r*c-n;
id=0;
for(int i=0; i<n; i++){
if(!vis[i]){
x1=Y1=INT_MAX;
x2=y2=INT_MIN;
++id;
dfs(i);
clac();
}
}
if(tot>0) res.push_back(tot);
sort(res.begin(), res.end());
printf("%lu\n", res.size());
for(size_t i=0; i<res.size(); i++){
if(i) putchar(' ');
printf("%lld", res[i]);
}
puts("");
}
int main(){
int T, cas=0;
for(cin>>T; T--; ){
printf("Case #%d:\n", ++cas);
cin>>r>>c>>n;
for(int i=0; i<n; i++){
scanf("%d%d", x+i, y+i);
}
solve(n);
}
return 0;
}
另外一个更隐蔽的坑:
DFS 的三个边界情况 (点阵边界, 超出矩形框, 坏点) 的顺序.
HDU 5925 Coconuts的更多相关文章
- HDU 5925 Coconuts 离散化
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5925 Coconuts Time Limit: 9000/4500 MS (Java/Others) ...
- HDU 5925 Coconuts 【离散化+BFS】 (2016CCPC东北地区大学生程序设计竞赛)
Coconuts Time Limit: 9000/4500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Su ...
- hdu 5925 Coconuts 离散化+dfs
Coconuts Time Limit: 9000/4500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Problem ...
- Coconuts HDU - 5925 (二维离散化求连通块的个数以及大小)
题目链接: D - Coconuts HDU - 5925 题目大意:首先是T组测试样例,然后给你n*m的矩阵,原先矩阵里面都是白色的点,然后再输入k个黑色的点.这k个黑色的点可能会使得原先白色的点 ...
- Coconuts HDU - 5925 二维离散化 自闭了
TanBig, a friend of Mr. Frog, likes eating very much, so he always has dreams about eating. One day, ...
- HDU 5925 离散化
东北赛的一道二等奖题 当时学长想了一个dfs的解法并且通过了 那时自己也有一个bfs的解法没有拿出来 一直没有机会和时ji间xing来验证对错 昨天和队友谈离散化的时候想到了 于是用当时的思路做了一下 ...
- 2016 长春东北赛---Coconuts(离散化+DFS)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5925 Problem Description TanBig, a friend of Mr. Frog ...
- HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
Basic Data Structure Time Limit: 7000/3500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Oth ...
- HDOJ 2111. Saving HDU 贪心 结构体排序
Saving HDU Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
随机推荐
- 一次莽撞的行为:在phpmyadmin中修改MySQL root密码后无法操作数据库
一.手贱行为(✿◡‿◡) 在一次开发中通过xampp方式安装了PHP环境,需要操作数据库时通过phpmyadmin访问MySQL,在一次数据库操作时想起没有设置密码,于是直接在mysql数据库中的us ...
- 从.NET的宠物商店到Android MVC MVP
1 一些闲话 记得刚进公司的时候,我们除了做常规的Training Project外,每天还要上课,接受各种技术培训和公司业务介绍.当时第一次知道QA和SQA的区别.Training Project时 ...
- 多线程处理中Future的妙用
java 中Future是一个未来对象,里面保存这线程处理结果,它像一个提货凭证,拿着它你可以随时去提取结果.在两种情况下,离开Future几乎很难办.一种情况是拆分订单,比如你的应用收到一个批量订单 ...
- swagger editor使用
swagger editor使用 swagger是一套开源的API设计工具,包括Swagger UI,Swagger Editor等. Swagger Editor 其中Swagger Editor是 ...
- 浅析WPhone、Android的Back与Home键
浅析WPhone.Android的Back与Home键 背景 本人一直在用诺基亚手机(目前是Nokia 925,Windows Phonre 8.1),在界面设计.应用多样性等方面没少受身边Andro ...
- Android视频播放之VideoView
Android视频播放之VideoView 1.VideoView类介绍 Android的VideoView组件可以从不同的来源(例如资源文件或内容提供器)读取图像,计算和维护视频的画面尺寸以使其适用 ...
- ContentProvider数据访问详解
ContentProvider数据访问详解 Android官方指出的数据存储方式总共有五种:Shared Preferences.网络存储.文件存储.外储存储.SQLite,这些存储方式一般都只是在一 ...
- [C#解惑] #2 对象的初始化顺序
谜题 在上一篇C#解惑中,我们提到了对象的初始化顺序.当我们创建一个子类的实例时,总是会先执行基类的构造函数,然后再执行子类的构造函数.那么实例字段是什么时候初始化的呢?静态构造函数和静态字段呢?今天 ...
- SNMP 原理与实战详解
原文地址:http://freeloda.blog.51cto.com/2033581/1306743 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法 ...
- Simple File System
This is my operating system class design. scanner.h #include<string> using namespace std; #pra ...