十字绣 题解

分析

首先能够一针解决的,肯定是在一个针线两端相连的连通块里,把所有连通块里的针数相加即可。
求连通块用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);
		}
	}
}
posted @ 2020-07-07 12:59  狂飙霹雳虎  阅读(242)  评论(0编辑  收藏  举报