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 ...
随机推荐
- Eclipse调试常用技巧(转)
Eclipse调试常用技巧 转自http://daimojingdeyu.iteye.com/blog/633824 1. 条件断点 断点大家都比较熟悉,在Eclipse Java 编辑区的行头双击就 ...
- QTableView 一列添加两个按钮
在QTableView的一列里添加两个按钮,之前添加一个按钮的思路是一样的,只是计算了一下按钮的宽,放两个按钮而已. 添加一个按钮的例子:QTableView 添加按钮 本例源代码:QtTowButt ...
- sencha xtype清单
xtype Class ----------------- --------------------- actionsheet Ext.ActionSheet audio Ext.Audio butt ...
- P值与significant(显著性)的理解
P值与significant的理解 来源:广州市统计局 发表日期:2015-01-21 P值可以理解为结论的风险大小,也就是根据数据得出的结果有多大的错误风险,P值越小,结论错误的风险越小 ...
- We Know What @You #Tag: Does the Dual Role Affect Hashtag Adoption-20160520
分析类的论文 1.Information publication:www2012 author: Mei qiao zhu 2.What 微博中的hashtag既可以表示谈论的内容,又可以代表一个群体 ...
- go println与printf区别
Println 与Printf 都是fmt 包中的公共方法 Println :可以打印出字符串,和变量: Printf : 只可以打印出格式化的字符串,可以输出字符串类型的变量,不可以输出整形变量和整 ...
- Collection中list集合的应用常见的方法
集合 : 用存放对象的容器(集合) Collection : 跟接口 : 单列集合 ---> List :有序的 ,元素是可以重复的. ---> ...
- Linux IO Scheduler(Linux IO 调度器)
每个块设备或者块设备的分区,都对应有自身的请求队列(request_queue),而每个请求队列都可以选择一个I/O调度器来协调所递交的request.I/O调度器的基本目的是将请求按照它们对应在块设 ...
- 链栈的C语言实现
/* 功能:栈的链表实现 Author:lxm Date: 20160511 */ #include <stdio.h> #include <stdlib.h> #define ...
- 【UOJ #20】【NOIP 2014】解方程
http://uoj.ac/problem/20 并不会做...然后看题解....... 对a取模,避免了高精度带来的复杂度,然后再枚举x判断是否满足模意义下等于0. 取5个模数,我直接抄的别人的_( ...