ZOJ 1654_Place the Robots(建立二分图)

二分图概念及其相关算法:http://www.renfei.org/blog/bipartite-matching.html

本题难在如何建立二分图,详情看注释:

//MJRT
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
//
#include<iostream>
#include<algorithm>
#include<string>
#include <iterator>
#include<sstream>
#include<functional>
#include<numeric>
///
#include<vector>
#include<map>
#include <stack>
#include<queue>
#include<set>
#include <bitset>
#include <list>
using namespace std;
#define lch(x)  ((x) << 1)
#define rch(x) ((x)<<1|1)
#define dad(x) ((x)>>1)
#define lowbit(x) ((x)&(-x))
static int INDEX = 0,BUGs = 0;
#define BUG() cout << "There is BUG No." << BUGs++ <<endl;
#define Whats(x) cout << "{ "<< #x << " }" << " is " << "*** "<< x << " ***" << "  index:" << INDEX++ <<endl;
#define Show(x,s,l)         cout << #x << ": "; for(int i = s ; i < s+l ; i++) cout << x[i] << " ";  cout << "\n";
#define Clear(name) memset(name, 0, sizeof(name));
typedef  long long int LL;
const int INF = ~0U>>1;
struct Point
{
    int x,y;
    Point(int x,int y):x(x),y(y){}
    Point(){}
};

struct Line
{
    Point s,e;
    Line(Point s,Point e):s(s),e(e){}
    Line(){}

    bool operator ^ (const Line & b) const
    {
        return
            max(s.x,e.x) >= min(b.s.x,b.e.x) &&
            max(b.s.x,b.e.x) >= min(s.x,e.x) &&
            max(s.y,e.y) >= min(b.s.y,b.e.y) &&
            max(b.s.y,b.e.y) >= min(s.y,e.y) ;
    }

};
const int N = 5 + 1250;
char maze[N][N];
bool mp[N][N];
int mark[N];
bool vis[N];
int m,n;

vector<Line> row,col;

bool dfs(int x);
int main()
{
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    int Case,tot=1;
    scanf("%d",&Case);
    while(Case--)
    {
        scanf("%d%d",&m,&n);

        Clear(maze);
        for(int i = 0 ;i < m ; i++)    scanf("%s",&maze[i]);
//计算横着的联通块(需包含空地o)
        row.clear();
        row.push_back(Line(Point(0,0),Point(0,0)));
        for(int i = 0 ;i < m ; i++)
        {
            bool ok = 0;
            for(int j = 0 ; j < n ; )
            {
                while(maze[i][j] == '#' && j < n) j++;
                int s = j;
                while(maze[i][j] != '#' && j < n) j++;
                int e = j-1;
                for(int k = s ; k <= e ; k++)    if(maze[i][k]=='o') {ok=1 ; break;}
                if(s == e && maze[i][s] != 'o')  ok = 0;
                if(ok&&s<=e)  row.push_back(Line(Point(i,s),Point(i,e)));
            }
        }
//计算竖着的联通块(需包含空地o)
        col.clear();
        col.push_back(Line(Point(0,0),Point(0,0)));
        for(int i = 0 ; i < n ; i++)
        {
            bool ok = 0;
            for(int j = 0 ; j < m ; )
            {
                while(maze[j][i] == '#' && j < m) j++;
                int s = j;
                while(maze[j][i] != '#' && j < m) j++;
                int e = j-1;
                for(int k = s ; k <= e ; k++)    if(maze[k][i]=='o') {ok=1 ; break;}
                if(s == e && maze[s][i] != 'o')  ok = 0;
                if(ok && s<=e )  col.push_back(Line(Point(s,i),Point(e,i)));
            }
        }
//建立二分图
//如果横着的联通块和竖着的联通块相交 且 交点是空地o ,我们认为他们是匹配的
        Clear(mp);
        for(int i = 1 ; i < row.size() ; i++ )
            for(int j = 1 ; j < col.size() ; j++ )
                if(row[i] ^ col[j] && maze[ row[i].s.x ][ col[j].s.y ] == 'o')
                    mp[i][j] = 1;

//为了方便重载n,m为二分图的长和宽
        m = row.size()-1;
        n = col.size()-1;

        int cnt = 0;
        Clear(mark);
        for(int i = 1 ; i <= m ;i++)
        {
            Clear(vis);
            if(dfs(i)) cnt++;
            //else Whats(i);
        }

        int res = cnt;
        printf("Case :%d\n",tot++);
        printf("%d\n",res);
    }
    return 0;
}

bool dfs(int x)
{
    for(int i = 1 ; i<= n ; i++)
    {
        if(!mp[x][i] || vis[i] )  continue;
        vis[i] = 1;
        if(!mark[i] || dfs(mark[i]))
        {
            mark[i] = x;
            return 1;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值