蛋糕被切成了几块
缘起
Tom 在一家蛋糕厂工作,他想知道工厂的蛋糕切割机的切割过程将蛋糕切成了几块. 你能帮帮他吗? poj 1371 Tin Cutter
分析
在一家锡罐头切割厂有一个切锡板的机器,该机器有一块异常锋利的刀锋,这块刀锋可以垂直或者水平的割痕,每次切割的
过程由若干割痕构成,每条割痕由起点和终点构成. 输入保证割痕的起点和终点总是在锡板中. 在切割的过程中, 锡板的
一些部分会脱落,所以锡板会出现一些洞洞. 锡罐头厂的管理层希望预知切割结束后锡板上的洞洞的数量. 你被要求
写一个程序完成这个任务. 注意,单条割痕是不会产生洞洞的.
下图是一些例子
【输入】
多样例. 每个样例开始于 n , n<=100 是割痕的条数,然后下面是 n 行, 每行是 x1 y1 x2 y2, 其中 (x1, y1)、
(x2, y2) 是割痕的起点和终点, 最后一个样例的 n = 0, 您不需要处理这组数据
【输出】
对于每组压样例输出洞的个数.
【样例输入】
4
0 1 1 1
1 1 1 0
1 0 0 0
0 0 0 1
2
0 1 2 1
1 2 1 0
0
【样例输出】
1
0
【限制】
1s 10MB
首先,题目并未说锡板的尺寸. 也就是说,我们并不知道伊始锡板的初始尺寸. 所以只能假设其实无限大. 其次因为割痕的坐标也没有说是不是整点,所以我们只能假定是浮点类型. 综上述,自然是要离散化的. 而且是x 方向和 y 方向都要离散化,离散化并不会破坏图的拓扑性质. 从而并不会影响最终的正确答案.
然后一个自然的想法是将所有会被割痕围住(包括割痕本身)的点置为黑点,不会被割痕围住的点就置为白点,最后跑一次泛洪(floodfill)就行了. 注意,对于下图(细黑线是割痕)
最后泛洪之后,黑点组成的块其实是不包含任何非割痕的点的,所以我们不能将这种块算进答案中.
所以判断一个点能不能被割痕围住是本题的关键.
如果从计算几何的角度考虑每个点,即给你一个点,然后给你 n 根线段(也就是割痕),然后判断这个点不会不会被某些线段围住其实是非常不好判断的. 例如下图展示的情况
P 就不好判断是否被线段围住,但是如果将思考的角度换成搜索的话,就一目了然了. 即首先将 n 根线段勾勒出来,然后从 (0, 0) 出发((0,0) 是离散化后的坐标点),线段视作是围墙,那么从 (0, 0) 出发遇到墙就进不去了. 通过一遍 dfs 或者 bfs 的方法就知道哪些点应该置为黑点了. 然后再跑一次泛洪就能计数有多少个黑块了,而这,就是答案.
所以本题如果站在计算几何的角度,就比较难解决,但是如果站在搜索的角度,就是一道水题了.
但是还有一个问题,我其实也想了蛮久. 就是下图这种情况
题目要求输出的是 2, 但是如果简单的使用泛洪,A点如果是视作是黑点的话(至少按照之前的说法,割痕上的点也要视作是黑点),则泛洪之后,锡板就只有 1 个洞了. 因为 P处的黑点 可以借助 A 来到 Q 处的黑点. 这就 WA 掉了. 但是如果将割痕上的点视作是非黑点的话,则对于下图泛洪之后就会得到错误的答案
因为中间的割痕视作是非黑点,所以会得到错误的答案2,但是显然答案应该是 1.
所以割痕不能简单的视作是黑点还是非黑点.
一个自然的规避图1困境的做法是
图2我想表达的意思是,将矩形往左下角缩一格. 即 图1中的 P 缩小为 P', 图 2中的 Q 缩小为 Q', 那么P' 和 Q' 之间肯定就没有交集了. 这样就能得到 2 的正确答案. 但是这种 缩一格 的处理会带来一个问题. 那就是如果 P 和 Q 原本就是单元格呢? 所谓单元格指的是边长为 1(这个1 指的是离散化之后的边长) 的正方形格子. 那就没法缩了呀! 所以我们不能让离散化之后的最小单位是 1,而应该是 2. 这样的话,原本的单元格就变成了边长是2 的正方形格子. 那么就总能进行 缩一格 的处理了. 为了能进行 缩一格 的处理,我们制定如下泛洪规则
P 点是白点的下一个泛洪过程中的移动点(图4箭头的意思是 泛洪过程中从白点移动到P),如果打对勾的边是割痕的话,则 P 是黑点(也就是组成洞的点) 其实你仔细看图4,就会发现 ①、② 刻画的是图2中 P 到 P' 上方的缩一格, 而 ③、④ 刻画的是图2中从 P 到 P' 右方的 缩一格.
而且如果做这种缩一格的话,图3的问题也就解决了——因为图3中的中间的割痕除去上边一个点之外是黑点无疑了.
//#define LOCAL
#pragma GCC optimize(2)
#pragma G++ optimize(2)
#pragma warning(disable:4996)
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <ctype.h>
#include <string.h>
#include <fstream>
#include <sstream>
#include <math.h>
#include <float.h>
#include <map>
//#include <unordered_map>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <time.h>
#include <stdlib.h>
#include <bitset>
using namespace std;
//#define int unsigned long long
//#define int long long
#define re register int
#define ci const int
#define ui unsigned int
typedef pair<int, int> P;
#define FE(cur) for(re h = head[cur], to; ~h; h = g[h].nxt)
#define ilv inline void
#define ili inline int
#define ilc inline char
#define ild inline double
#define ilp inline P
#define LEN(cur) (hjt[cur].r - hjt[cur].l)
#define MID(cur) (hjt[cur].l + hjt[cur].r >> 1)
#define SQUARE(x) ((x) * (x))
typedef vector<int>::iterator vit;
typedef set<int>::iterator sit;
typedef map<int, int>::iterator mit;
const int inf = ~0u >> 1;
//const int inf = 0x3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-8;
namespace fastio
{
const int BUF = 1 << 21;
char fr[BUF], fw[BUF], * pr1 = fr, * pr2 = fr; int pw;
ilc gc() { return pr1 == pr2 && (pr2 = (pr1 = fr) + fread(fr, 1, BUF, stdin), *pr2 = 0, pr1 == pr2) ? EOF : *pr1++; }
ilv flush() { fwrite(fw, 1, pw, stdout); pw = 0; }
ilv pc(char c) { if (pw >= BUF) flush(); fw[pw++] = c; }
ili read(int& x)
{
x = 0; int f = 1; char c = gc(); if (!~c) return EOF;
while (!isdigit(c)) { if (c == '-') f = -1; c = gc(); }
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = gc();
x *= f; return 1;
}
ili read(double& x)
{
int xx = 0; double f = 1.0, fraction = 1.0; char c = gc(); if (!~c) return EOF;
while (!isdigit(c)) { if (c == '-') f = -1.0; c = gc(); }
while (isdigit(c)) { xx = (xx << 3) + (xx << 1) + (c ^ 48), c = gc(); }
x = xx;
if (c ^ '.') { x = f * xx; return 1; }
c = gc();
while (isdigit(c)) x += (c ^ 48) * (fraction /= 10), c = gc();
x *= f; return 1;
}
ilv write(int x) { if (x < 0) pc('-'), x = -x; if (x > 9) write(x / 10); pc(x % 10 + 48); }
ilv writeln(int x) { write(x); pc(10); }
ili read(char* x)
{
char c = gc(); if (!~c) return EOF;
while (!isalpha(c) && !isdigit(c)) c = gc();
while (isalpha(c) || isdigit(c)) *x++ = c, c = gc();
*x = 0; return 1;
}
ili readln(char* x)
{
char c = gc(); if (!~c) return EOF;
while (c == 10) c = gc();
while (c >= 32 && c <= 126) *x++ = c, c = gc();
*x = 0; return 1;
}
ilv write(char* x) { while (*x) pc(*x++); }
ilv write(const char* x) { while (*x) pc(*x++); }
ilv writeln(char* x) { write(x); pc(10); }
ilv writeln(const char* x) { write(x); pc(10); }
ilv write(char c) { pc(c); }
ilv writeln(char c) { write(c); pc(10); }
} using namespace fastio;
const int maxn = 210, dir[4][2] = { -1, 0, 1, 0, 0, -1, 0, 1 }, maxm = maxn << 1;
int n, x[maxn], y[maxn], xx[maxn], yy[maxn], v[maxm][maxm], z[maxm][maxm];
ilv discrete(int* x, int* xx, int n)
{
memcpy(xx, x, sizeof(int) * n);
sort(xx + 1, xx + n);
int nn = unique(xx + 1, xx + n) - xx;
for (re i = 1; i <= n; i++)
{
x[i] = (lower_bound(xx + 1, xx + nn + 1, x[i]) - xx) << 1;
}
}
ilv drawLine()
{
for (re i = 1, s, t, k; i <= (n << 1); i += 2)
{
if (x[i] == x[i + 1])
{
k = x[i], s = max(y[i], y[i + 1]), t = min(y[i], y[i + 1]);
for (re j = t; j <= s; j++)
{
v[k][j] = 1;
}
}
else
{
k = y[i], s = max(x[i], x[i + 1]), t = min(x[i], x[i + 1]);
for (re j = t; j <= s; j++)
{
v[j][k] = 1;
}
}
}
}
ilv dfs(int s, int t)
{
z[s][t] = 1;
for (re i = 0, a, b; i < 4; i++)
{
a = s + dir[i][0];
b = t + dir[i][1];
if (a < 0 || a >= maxm || b < 0 || b >= maxm || z[a][b])
{
continue;
}
if (i == 0 && v[a + 1][b] && v[a + 1][b + 1])
{
continue;
}
if (i == 1 && v[a][b] && v[a][b + 1])
{
continue;
}
if (i == 2 && v[a][b + 1] && v[a + 1][b + 1])
{
continue;
}
if (i == 3 && v[a][b] && v[a + 1][b])
{
continue;
}
dfs(a, b);
}
}
ilv DFS(int s, int t)
{
z[s][t] = 1;
for (re i = 0, a, b; i < 4; i++)
{
a = s + dir[i][0];
b = t + dir[i][1];
if (z[a][b])
{
continue;
}
DFS(a, b);
}
}
signed main()
{
#ifdef LOCAL
FILE* ALLIN = freopen("d:\data.in", "r", stdin);
// freopen("d:\my.out", "w", stdout);
#endif
while (read(n), n)
{
memset(v, 0, sizeof(v));
memset(z, 0, sizeof(z));
for (re i = 1; i <= (n << 1); i += 2)
{
read(x[i]), read(y[i]);
read(x[i + 1]), read(y[i + 1]);
}
discrete(x, xx, n << 1 | 1);
discrete(y, yy, n << 1 | 1);
drawLine();
dfs(0, 0);
int ans = 0;
for (re i = 0; i < maxm; i++)
{
for (re j = 0; j < maxm; j++)
{
if (!z[i][j])
{
++ans;
DFS(i, j);
}
}
}
writeln(ans);
}
flush();
#ifdef LOCAL
fclose(ALLIN);
#endif
return 0;
}
ac 情况
Accepted 390ms 5144B
- 参考基因组没有,经费也没那么多,怎么办?
- .Net下SQLite的DBHelp
- 数据库进程间通信解决方案之MQ
- 【学术】算法交易的神经网络:强化经典策略
- java.util.logging 例子
- WPF命令(Command)介绍、命令和数据绑定集成应用
- lncRNA实战项目-第六步-WGCNA相关性分析
- 【项目】Github上的一个简单项目:用人工智能预测大学录取概率
- lncRNA实战项目-第五步-差异表达的mRNA和lncRNA
- WPF--模板选择
- 高级运维工程师面试题(更新中)
- Nginx rewrite 获取问好“?”后面的参数
- 微信公众平台开放JS-SDK(微信内网页开发工具包)
- 数据库与图片完美解决方案
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 高效、简单、方便管理与维护的开源运维工单系统
- python数据库入门编程。
- EasyExcel导出自定义合并单元格策略
- CMake学习笔记
- 性能分析(2)- 应用程序 CPU 使用率过高案例
- 性能测试必备知识(7)- 深入理解“CPU 使用率”
- Jmeter 常用函数(31)- 详解 __iterationNum
- Jmeter 常用函数(30)- 详解 __if
- Jmeter 常用函数(29)- 详解 __eval
- Jmeter 常用函数(28)- 详解 __FileToString
- Jmeter 常用函数(26)- 详解 __chooseRandom
- Jmeter 常用函数(25)- 详解 __V
- Java8新特性
- 线程池基础
- 多线程基础