poj2446 2010.2.21

本文解析了POJ2446题目,通过构建二分图进行匹配来判断棋盘上的正常格子是否能够被特定规则的卡片完全覆盖。介绍了两种不同的实现方法,并提供了完整的代码示例。

poj2446 2010.2.21

http://blog.chinaunix.net/u3/102624/showart_2060827.html

 

题意:

 

      玩个游戏:给出一个m行n列的棋盘,里面有m*n个方格,其中有k个格子上有洞,我们称那些没洞的格子叫正常的格子(normal grid),Bob要遵循两个规则去玩:(1)任何一个正常的格子都要被一张卡覆盖,(卡片是1*2规格的)(2)一张卡要正好覆盖两个相邻的正常格子

我们的任务是帮助Bob决定是否棋盘在上述两个规则下能被覆盖。

 

思路:

因为棋盘上都是两个格子放一张卡片,所以到最后肯定是两个点两个点连着的。由此想到了二分匹配,具体是这样的:


给每个格子编号,从第一行到最后一行编号为1—12 ,然后每个点跟临近的正常点连接,这就建成了二分图,如右上图。

然后以此建邻接表,建表时,枚举每个点,如果是正常点i,那么与他相邻的正常点(v)的邻接点数增一(g[v][0]++),并使g[v][g[v][0]] = i;

然后就是二分匹配模版了,完后看匹配数是否等于正常格子数,即是否能构成完美匹配。

 

 

wa

#include <stdio.h>
#include <string.h>

#define MAXN 400+10

int map[MAXN][MAXN];
int mid[MAXN][MAXN];
int link[MAXN],f[MAXN];
int n,m,num1,num2,ans=0;

int path(int t)
{
	int i;
	for(i=1;i<=n;i++)
	{
		if (f[i]==0&&map[t][i])
		{
			f[i]=1;
			if(link[i]==-1||path(link[i]))
			{
				link[i]=t;
				return 1;
			}
		}
	}
	return 0;
}

int EK()
{
	int sum=0;
	int i;
	memset(link,-1,sizeof(link));
	for(i=1;i<=n;i++)
	{
		memset(f,0,sizeof(f));
		if(path(i)) sum++;
	}
	return sum;
}

int main()
{
	scanf("%d %d %d",&num1,&num2,&m);
	int i,j;
	memset(map,0,sizeof(map));
	memset(mid,1,sizeof(mid));
	for(i=1;i<=num1;i++)
	{
		mid[i][0]=0;
		mid[i][num2+1]=0;
	}
	for(i=1;i<=num2;i++)
	{
		mid[0][i]=0;
		mid[num1+1][i]=0;
	}
	n=num1*num2-m;
	int a,b;
	for(i=1;i<=m;i++)
	{
		scanf("%d %d",&a,&b);
		mid[b][a]=0;
	}
	for(i=1;i<=num1;i++)
		for(j=1;j<=num2;j++)
			if (mid[i][j])
			{
				ans++;
				mid[i][j]=ans;
			}
	for(i=1;i<=num1;i++)
		for(j=1;j<=num2;j++)
			if (mid[i][j])
			{
				if (mid[i-1][j])
					map[mid[i-1][j]][mid[i][j]]=1;
				if (mid[i+1][j])
					map[mid[i+1][j]][mid[i][j]]=1;
				if (mid[i][j-1])
					map[mid[i][j-1]][mid[i][j]]=1;
				if (mid[i][j+1])
					map[mid[i][j+1]][mid[i][j]]=1;
			}
	if (EK()==n)printf("YES\n");
		else printf("NO\n");
	return 0;
}

标称:

#include <stdio.h>
#include <string.h>
#include <conio.h>
#define N 34
#define M N*N

int g[M][5], used[M], mat[M];
int match, m, n;

int find(int k)
{
    int i, j;

    for(i=1; i<=g[k][0]; i++)
    {
        j = g[k][i];
        if(!used[j])
        {
            used[j] = 1;
            if(!mat[j] || find(mat[j]))
            {
                mat[j] = k;
                return 1;
            }
        }
    }
    return 0;
}

void hungary()
{
    int i;
    for(i=1; i<=m*n; i++)
    {
        if(g[i][0] != -1 && g[i][0] != 0)
        {
            match += find(i);
            memset(used, 0, sizeof(used));
        }
    }
}

int main()
{
    int i, j;
    int k;
    int x, y;
    freopen("in.txt", "r", stdin);
    scanf("%d%d%d", &m, &n, &k);

    for(i=1; i<=k; i++)
    {
        scanf("%d%d", &x, &y);
        g[x+(y-1)*n][0] = -1;
    }
    for(i=1; i<=m*n; i++)
    {
        if(g[i][0] != -1)
        {
            //left

            if((i-1)%n >= 1 && g[i-1][0] != -1)
                g[i-1][++g[i-1][0]] = i;
            //right

            if(i%n != 0 && g[i+1][0] != -1)
                g[i+1][++g[i+1][0]] = i;
            //up

            if((i-(i%n)) / n >= 1 && g[i-n][0] != -1)
                g[i-n][++g[i-n][0]] = i;
            //down

            if((i-(i%n)+1) / n <= m && g[i+n][0] != -1)
                g[i+n][++g[i+n][0]] = i;
        }
    }
    match = 0;
    hungary();
    
    if(match == m*n-k)
        printf("YES\n");
    else printf("NO\n");
    //printf("%d\n", match);

    getch();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值