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. 详解c++指针的指针和指针的引用

    展示一下使用指针的指针和指针的引用修改传递给方法的指针,以便更好的使用它.(这里说的指针的指针不是一个二维数组) 为什么需要使用它们 当我们把一个指针做为参数传一个方法时,其实是把指针的复本传递给了方 ...

  2. 转载:ZooKeeper Programmer's Guide(中文翻译)

    本文是为想要创建使用ZooKeeper协调服务优势的分布式应用的开发者准备的.本文包含理论信息和实践信息. 本指南的前四节对各种ZooKeeper概念进行较高层次的讨论.这些概念对于理解ZooKeep ...

  3. 20160803 - C:\WINDOWS\system32\config\systemprofile\Desktop 不可用的解决

    问题:某些软件在从注册表读取用户桌面地址时,欠考虑的%USERPROFILE%的情况,例如迅雷打开文件时,会提示: [Window Title]位置不可用 [Content]C:\WINDOWS\sy ...

  4. 20160205 - Windows 10 家庭版没有组策略

    问题描述:买笔电自带的正版系统,有一个需要使用gpedit.msc,发现并不存在. 解决办法:升级到 Windows 10 专业版

  5. Windows Phone 8 下载文件进度

    后台代码: public partial class MainPage : PhoneApplicationPage { private long siz; private long speed; p ...

  6. Codeforces Round #369(div 2)

    A:=w= B:=w= C:题意:有一排树,有的树已经上色,有的树没有上色,只能给没上色的树上色,一共m种颜色,不同的树上不同的色花费不同,涂完色后,连续颜色的树成为一段.对于给定的段数k,求出最小花 ...

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

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

  8. [BZOJ1193][HNOI2006]马步距离(贪心+dfs)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1193 分析: 首先小范围可以直接暴力.(其实只要用上题目中的表就行了) 如果范围比较大 ...

  9. matlab 绘制条形图

    Matlab使用bar和barh函数来绘制二维条形图.分别是绘制二维垂直条形图和二维水平条形图. 转自:http://jingyan.baidu.com/article/64d05a02524e63d ...

  10. mvc的自带json序列化的datetime在js中的解析

    默认仅序列化后的日期格式是这样的:'/Date(124565787989)/'(数字随便敲的,数字表示相对于1970年的总毫秒数) 在js中借助eval函数,eval函数的意义:将参数中的字符串当作j ...