• 十字绣 题解


    分析

    首先能够一针解决的,肯定是在一个针线两端相连的连通块里,把所有连通块里的针数相加即可。
    求连通块用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);
    		}
    	}
    }
    
  • 相关阅读:
    vs错误集合及解决方案
    使用内存映射文件进行EXE、DLL通信(非MFC)
    visual studio使用小技巧(以vs2012为例)
    GetModuleHandle(NULL)获取当前DLL模块基址?
    格式化输出中的%s和%S的陷阱
    关于字符编码
    远程附加调试服务的方法
    结构体内包含位段,其数据内存分布
    微信个人公众号开发-java
    Docker-常用基建的安装与部署
  • 原文地址:https://www.cnblogs.com/kuangbiaopilihu/p/13260158.html
Copyright © 2020-2023  润新知