2016 CCPC 东北四省赛 D.

一道好题.

现场写崩了.

赛后LSh跟我讲了一种离散化的做法, 没听懂.

题意

一个$R \cdot C\ (R, C\le 10^9)$ 的矩形点阵上有 $n \ (n \le 200) $ 个坏点, 其余都是好点.

好点的4-连通块 (以下简称cell) 的个数和每块的大小.

做法

我的想法是:

找出一个坏点的8-连通块, 在框住这个块的(最小)矩形区域内, 搜索当前8-连通块以及整个矩形点阵的边界 (以下简称"fence") 包围的cell.

这个想法大体上没什么漏洞.

要注意的问题有

  1. fence 套 fence 的情况
  2. 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数组+全局变量

一个好点的连通块合法的条件:

  1. 不超出矩形框
  2. 至少能遇到一个当前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的更多相关文章

  1. HDU 5925 Coconuts 离散化

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5925 Coconuts Time Limit: 9000/4500 MS (Java/Others) ...

  2. HDU 5925 Coconuts 【离散化+BFS】 (2016CCPC东北地区大学生程序设计竞赛)

    Coconuts Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Su ...

  3. hdu 5925 Coconuts 离散化+dfs

    Coconuts Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem ...

  4. Coconuts HDU - 5925 (二维离散化求连通块的个数以及大小)

    题目链接: D - Coconuts  HDU - 5925 题目大意:首先是T组测试样例,然后给你n*m的矩阵,原先矩阵里面都是白色的点,然后再输入k个黑色的点.这k个黑色的点可能会使得原先白色的点 ...

  5. Coconuts HDU - 5925 二维离散化 自闭了

    TanBig, a friend of Mr. Frog, likes eating very much, so he always has dreams about eating. One day, ...

  6. HDU 5925 离散化

    东北赛的一道二等奖题 当时学长想了一个dfs的解法并且通过了 那时自己也有一个bfs的解法没有拿出来 一直没有机会和时ji间xing来验证对错 昨天和队友谈离散化的时候想到了 于是用当时的思路做了一下 ...

  7. 2016 长春东北赛---Coconuts(离散化+DFS)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5925 Problem Description TanBig, a friend of Mr. Frog ...

  8. HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)

    Basic Data Structure Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Oth ...

  9. HDOJ 2111. Saving HDU 贪心 结构体排序

    Saving HDU Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

随机推荐

  1. Java学习笔记(二二)——Java HashMap

    [前面的话] 早上起来好瞌睡哈,最近要注意一样作息状态.       HashMap好好学习一下. [定义] Hashmap:是一个散列表,它存储的内容是键值对(key——value)映射.允许nul ...

  2. 创建多个Oracle数据库及相应的实例

    转 http://blog.csdn.net/luiseradl/article/details/6972217 对于使用过SQL Server数据库的用户可以会对Oracle中的数据库的实例的概念理 ...

  3. [poj2484]A Funny Game(对称博弈)

    题目:http://poj.org/problem?id=2484 题意:n个石子围成一个圈,两个人轮流取,每次可以取一个石子或者相邻的两个石子,问先手胜还是后手胜 分析: 典型的对称博弈 如果n&g ...

  4. [POJ3696]The Luckiest number(数论)

    题目:http://poj.org/problem?id=3696 题意:给你一个数字L,你要求出一个数N,使得N是L的倍数,且N的每位数都必须是8,输出N的位数(如果不存在输出0) 分析: 首先我们 ...

  5. Crowdsourcing(众包)

    群众外包(英语:crowdsourcing)是互联网带来的新的生产组织形式.<连线>(Wired)杂志记者Jeff Howe于2006年发明的一个专业术语,用来描述一种新的商业模式,即企业 ...

  6. jquery里面的$(this)和this都什么时候用,有什么区别

    当你用的是jquery时,就用$(this),如果是JS,就用this $(this).html( $(this).html() + " BAM! " + i ); 这个里的htm ...

  7. spring配置属性的两种方式

    spring配置属性有两种方式,第一种方式通过context命名空间中的property-placeholder标签 <context:property-placeholder location ...

  8. Beta版本冲刺Day5

    会议讨论: 628:配置java环境已经成功,Tomcat部署也成功了,Mysql还未进行配置.601:继续修改其他的页面外观. 528:继续完成其他的功能页面. 340:对一些页面进行了优化美观,并 ...

  9. 1104关于优化mysql服务器几个参数和思路

    转自http://www.cnblogs.com/AloneSword/p/3207697.html 按照从大到小,从主要到次要的形式,分析 mysql 性能优化点,达到最终优化的效果. 利用 min ...

  10. Linux下解决用户不能执行sudo的方法

    报错: xxx is not in the sudoers file.  This incident will be reported. Linux默认没有为当前用户开启sudo权限! $ su  $ ...