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的情况已经保护了。