棋盘问题总结

我们在研究问题时经常能碰到这一类为题:在某个棋盘上有一种棋子,问最多放下几个棋子。或者有一堆棋子,问你移去最少的棋子数目,使得剩下来的棋子两两不攻击。

先看下面这道题

问题 E: P1035

时间限制: 1 Sec  内存限制: 128 MB
提交: 82  解决: 35
[提交][状态][讨论版]

题目描述

给出一张n*n(n< =100)的国际象棋棋盘,其中被删除了一些点,问可以使用多少1*2的多米诺骨牌进行掩盖。

输入

第一行为n,m(表示有m个删除的格子) 第二行到m+1行为x,y,分别表示删除格子所在的位置 x为第x行 y为第y列 

输出

一个数,即最大覆盖格数

样例输入

8 0

样例输出

32

这道题先想的是状压DP(爆搜)后来不会做啦。

其实一个棋子只能和它上下左右4块中的一块拼成一大块,所以我们把每个棋子与其上下左右四个棋子连一条边。连完所有的边后,我们发现要求的就是最多有多少的边(顶点不能重复)想到了什么?二分图匹配。但它不一定是个二分图啊。我们需要证明这一点。一种口糊的方法就是显然一个点仅走奇数次肯定不能回到原点,所以原图中不存在奇数边的环路,就是二分图啦。还有一种比较巧妙和通用的方法就是将棋盘黑白染色,所有黑的只能与白点发生关系,就是显然的二分图了(黑白两个点集)。对于棋子的问题,我们只要在相互冲突的棋子连上一条边,然后简单的证明一下是不是二分图,最后求最大子独立集或最大匹配即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define grey 2
#define black 1
#define white 0
int sum,n,m,Map[105][105],color[105][105],num_white,num_black,White[105],Black[10005],used[10005],match[10005];
bool dfs(int u){
      int t,i;
      for (int v=1;v<=num_white;v++)
      {
              i=White[v];
              if (used[i]==0 && Map[u][i])
              {
                          
                        used[i]=1;
                        t=match[i];
                        match[i]=u;
                        if (t==0 || dfs(t)) return 1;
                        match[i]=t;
             }
             
          }
      return 0;
}
void make_way(int u,int v)
{
    Map[u][v]=1;
}
int make_num(int a,int b)
{
    return ((n)*(a-1)+b);
}
void make_edge()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(color[i][j]!=grey)
        {
            if(color[i][j]==white)
            {
                White[++num_white]=make_num(i,j);
            }else Black[++num_black]=make_num(i,j);
            if(i>1&&color[i-1][j]!=grey) make_way(make_num(i,j),make_num(i-1,j));
            if(j>1&&color[i][j-1]!=grey) make_way(make_num(i,j),make_num(i,j-1));
            if(i<n&&color[i+1][j]!=grey) make_way(make_num(i,j),make_num(i+1,j));
            if(j<n&&color[i][j+1]!=grey) make_way(make_num(i,j),make_num(i,j+1));
        }
}
void draw(int n)
{
    for(int i=1;i<=n;i++)
    {
        if(i%2) color[i][1]=black;else color[i][1]=white;  
        for(int j=2;j<=n;j++)
        {
            color[i][j]=1^color[i][j-1];
        }
    }
}
void print()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        cout<<color[i][j];
        cout<<endl;
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    draw(n);
    int a,b;
    for(int i=1;i<=m;i++)
    scanf("%d %d",&a,&b),color[a][b]=grey;
    make_edge();
    //print();
    for(int i=1;i<=num_black;i++)
    {
        memset(used,0,sizeof(used));
        if(dfs(Black[i]))sum++;
        //cout<<Black[i]<<endl;
    }
    cout<<sum<<endl;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define grey 2
#define black 1
#define white 0
int sum,n,m,Map[105][105],color[105][105],num_white,num_black,White[105],Black[10005],used[10005],match[10005];
bool dfs(int u){
      int t,i;
      for (int v=1;v<=num_white;v++)
      {
              i=White[v];
              if (used[i]==0 && Map[u][i])
              {
                          
                        used[i]=1;
                        t=match[i];
                        match[i]=u;
                        if (t==0 || dfs(t)) return 1;
                        match[i]=t;
             }
             
          }
      return 0;
}
void make_way(int u,int v)
{
    Map[u][v]=1;
}
int make_num(int a,int b)
{
    return ((n)*(a-1)+b);
}
void make_edge()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        if(color[i][j]!=grey)
        {
            if(color[i][j]==white)
            {
                White[++num_white]=make_num(i,j);
            }else Black[++num_black]=make_num(i,j);
            if(i>1&&color[i-1][j]!=grey) make_way(make_num(i,j),make_num(i-1,j));
            if(j>1&&color[i][j-1]!=grey) make_way(make_num(i,j),make_num(i,j-1));
            if(i<n&&color[i+1][j]!=grey) make_way(make_num(i,j),make_num(i+1,j));
            if(j<m&&color[i][j+1]!=grey) make_way(make_num(i,j),make_num(i,j+1));
        }
}
void draw(int n)
{
    for(int i=1;i<=n;i++)
    {
        if(i%2) color[i][1]=black;else color[i][1]=white;  
        for(int j=2;j<=n;j++)
        {
            color[i][j]=1^color[i][j-1];
        }
    }
}
void print()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        cout<<color[i][j];
        cout<<endl;
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    draw(n);
    int a,b;
    for(int i=1;i<=m;i++)
    scanf("%d %d",&a,&b),color[a][b]=grey;
    make_edge();
    //print();
    for(int i=1;i<=num_black;i++)
    {
        memset(used,0,sizeof(used));
        if(dfs(Black[i]))sum++;
        //cout<<Black[i]<<endl;
    }
    cout<<sum<<endl;
}
原文地址:https://www.cnblogs.com/dancer16/p/7208578.html