分析
首先能够一针解决的,肯定是在一个针线两端相连的连通块里,把所有连通块里的针数相加即可。
求连通块用dfs或者并查集即可。由于是二维坐标,还是转换成一维的点的编号比较方便。
下面我们就可以把原图转换成一个无向图,求连通块数。
每个连通块里的真数怎么求?
针的穿入穿出是在结点的位置,对于某一个结点:
如果既有一条正面的线,也有一条反面的线,对于该结点我们可以把它当做一针;
如果有两条正面的线,一条反面的线,那么对于该结点至少需要两针才能搞定;
我们扩展到一个连通块,每个结点都会有对应的最少针数,即为正面反面线数量的差绝对值。
我们可以累加起来,但是由于每一条线对应两个端点,因此一条线会对两个端点的针数做贡献,所以我们最后将总和除以2。
特别的,如果总和为0,我们可以认为是出现了一个环形的结构,即一针搞定,所以针数是加1.
// 最后求解的代码
for (int i = 1; i <= n+1; ++i) {
for (int j = 1; j <= m+1; ++j) {
if (!vis[id[i][j]] && flag[id[i][j]]) { // 没有访问过并且有线
sum = 0;
dfs(id[i][j]);
if (sum) ans += (sum >> 1);
else ++ans; // 说明一针穿一个环
}
}
}
// 加边函数,形参w用来表示正面还是反面的线,分别用1和-1表示
void addedge(int from, int to, int w) {
flag[from] = true;
e[++tot].to = to;
e[tot].next = head[from];
head[from] = tot;
// 记录正反面的针数
if (w == 1) {
++zheng[from]; // 只加from就行,因为还会反向加一次边
} else {
++fan[from];
}
}
// 建图的过程
for (int i = 1; i <= n; ++i) {
scanf("%s", str+1);
for (int j = 1; j <= m; ++j) {
if (str[j] == '/') { // 右上到左下建双向边
addedge(id[i][j+1], id[i+1][j], 1); // 这里1表示是正面的边
addedge(id[i+1][j], id[i][j+1], 1);
} else if (str[j] == '\') {
addedge(id[i][j], id[i+1][j+1], 1);
addedge(id[i+1][j+1], id[i][j], 1);
} else if (str[j] == 'X') {
addedge(id[i][j], id[i+1][j+1], 1);
addedge(id[i+1][j+1], id[i][j], 1);
addedge(id[i][j+1], id[i+1][j], 1);
addedge(id[i+1][j], id[i][j+1], 1);
}
}
}