欧拉路径+floodfill 十字绣

Description

考古学家发现了一块布,布上做有针线活,叫做“十字绣”,即交替地在布的两面穿线。
布是一个n*m的网格,线只能在网格的顶点处才能从布的一面穿到另一面。每一段线都覆盖一个单位网格的两条对角线之一,而在绣的过程中,一针中连续的两段线必须分处布的两面。给出布两面的图案(实线代表该处有线,虚线代表背面有线),问最少需要几针才能绣出来?一针是指针不离开布的一次绣花过程。

Input

第1行两个数N和M(1<=N,M<=200)。
接下来N行每行M个数描述正面。
再接下来N行每行M个数描述反面。
每个格子用.(表示空),/(表示从右上角连到左下角),\(表示从左上角连到右下角)和X(表示连两条对角线)表示。

Output

一个数,最少要用的针

Sample Input

4 5
.....
.\...
..\..
.....
.....
....\
.\X..
.....

Sample Output

4

 

解法分析

将题目数据 N X M 的图案,转化为 N+1 X M+1 规格的点,统计时每个点需要统计(x, y)、(x, y+1)、(x+1, y)、(x+1, y+1)上的方案,所以初始数据需要补充0、N+1行和0、M+1列,初始化为0即可,初始数据放在1~N行、1~M列里。对每个点统计至少需要的针数,根据欧拉路径,即为正反两面线数差的绝对值。

(欧拉路径补充)对每个连通块分别求针数。对于某个顶点i ,如果 |正边数-负边数| = K > 0,那么以该顶点为开始或结束的针数必然 >= K。对于配对的正负边,由于以该点为起点或终点的一针,可以与另一以该点为终点或起点的一针合并为一个,所以可以达到恰好为K针的方案。

再用floodfill的方法遍历,把所有K值加起来,除以2(每一针有两个端点)。注意差值为0时,需要特殊处理,加1针。

(floodfill补充)图形学中Flood   Fill是满水法填充,是用来填充区域的。就好比在一个地方一直到水,水会往四周满延开,直到高地阻挡。Flood   Fill就是从一个点开始往四周寻找相同的点填充,直到有不同的点为止。

算法中我们用的Flood   Fill和这个差不多原理,就是BFS的一种形式。假设在(i, j)滴好大一滴红墨水,然后水开始漫开,向它的上下左右染色,也就是(i-1, j),(i+1, j),(i, j-1),(i, j+1)这四个点。然后在分别再从这四个点开始向周围染色...直到碰到某种边界为止。

把这个转化为BFS的思想,就是队列中初始元素是(i, j),然后把(i, j)扩展状态,得到(i-1, j),(i+1, j),(i, j-1),(i, j+1)这四个状态,加入队列。把(i, j)出列,继续扩展下一个结点...如此。

 

#include<stdio.h>
#include<math.h>

typedef struct
{
	int x,y;
}A;

char fp[202][202],fn[202][202],rp[202][202],rn[202][202];
char v[202][202],d[202][202];
A q[40001];
int N,M,c;

void Pm(char p[202][202],char n[202][202])
{
	char s[200];
	int i,j;
	for (i=1;i<=N;i++)
	{
		scanf("%s",s);
		for (j=0;j<M;j++)
		{
			if (s[j]=='/' || s[j]=='X')p[i][j+1]=1;
			if (s[j]=='\\' || s[j]=='X')n[i][j+1]=1;
		}
	}
}

void flodfill(int x,int y)
{
	int b=0,e=1;
	q[0].x=x,q[0].y=y;
	v[x][y]=1;
	c+=d[x][y];	
	while(b<e)
	{
		x=q[b].x,y=q[b].y;
		if ((fn[x][y] || rn[x][y]) && x && y && (!v[x-1][y-1]))
		{
			v[x-1][y-1]=1;
			c+=d[x-1][y-1];
			q[e].x=x-1,q[e++].y=y-1;
		}
		if ((fn[x+1][y+1] || rn[x+1][y+1]) && (!v[x+1][y+1]))
		{
			v[x+1][y+1]=1;
			c+=d[x+1][y+1];
			q[e].x=x+1,q[e++].y=y+1;
		}
		if ((fp[x+1][y] || rp[x+1][y]) && y && (!v[x+1][y-1]))
		{
			v[x+1][y-1]=1;
			c+=d[x+1][y-1];
			q[e].x=x+1,q[e++].y=y-1;
		}
		if ((fp[x][y+1] || rp[x][y+1]) && x && (!v[x-1][y+1]))
		{
			v[x-1][y+1]=1;
			c+=d[x-1][y+1];
			q[e].x=x-1,q[e++].y=y+1;
		}
		b++;
	}
}

int main()
{
	int i,j,f,r,a=0;
	scanf("%d %d",&N,&M);
	Pm(fp,fn);
	Pm(rp,rn);

	for (i=0;i<=N;i++)
	{
		for (j=0;j<=M;j++)
		{
			f=0,r=0;
			if (fn[i][j])f++;		if (rn[i][j])r++;
			if (fn[i+1][j+1])f++;	if (rn[i+1][j+1])r++;
			if (fp[i+1][j])f++;		if (rp[i+1][j])r++;
			if (fp[i][j+1])f++;		if (rp[i][j+1])r++;
			if (f+r==0)v[i][j]=1;	else d[i][j]=abs(f-r);
		}
	}

	for (i=0;i<=N;i++)
	{
		for (j=0;j<=M;j++)
		{
			if (!v[i][j])
			{
				c=0;
				flodfill(i,j);
				if(!c)a++;
				else a+=c/2;
			}
		}
	}

	printf("%d",a);
}


 

 注:

char v[202][202]也定义为202,是避免在floodfill中!v[x+1][y+1])访问越界,虽然VC数组小范围越界没有事情,v[N][M]、v[-1][-1]都能访问。

if ((fn[x][y] || rn[x][y]) && x && y && (!v[x-1][y-1]))对x为0、y为0的情况已经保护了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值