hunnu OJ 11564 Easy Delete(二维坐标的离散化处理+最大匹配)

题意:•给出两类点坐标

–一类可删除

–一类不可删除

•选出最少的行或列,删除所有要删除的点。

 

思路

•首先看到坐标范围很大,第一点要做的就是离散化。

•接着,将行看成二分图的X集合,将列看成二分图的Y集合,于是,我们要删除的点,就是X连接Y的一条边。

•最后,我们要求的就是最少的点覆盖所有的边。在二分图中,最大匹配==最小点覆盖。

需要注意的地方

•判断每一个要删除的点,如果X方向和Y方向都有不能删除的点,就是Sorry。

•如果只有一个方向有,则必须使用另外一个方向删除该行/列的所有点。

剩下的点使用二分图最大匹配

 

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

#define clr(a,b) memset(a,b,sizeof(a))
const int maxn = 1005;

struct H
{
    int x,y,id,z;
}no[maxn];

bool cmp1(H a,H b){return a.x<b.x;}
bool cmp2(H a,H b){return a.y<b.y;}

int nx[maxn],ny[maxn],nz[maxn];
int mp[maxn][maxn],mm[maxn][maxn];

int vx[maxn],vy[maxn];
int cx[maxn],cy[maxn];
int vis[maxn],sign[maxn];
int cnx,cny;

int dfs(int x)
{
    for(int i=0;i<cny;i++)
    {
        if(!vis[i]&&mp[x][i])
        {
            vis[i]=1;
            if(sign[i] == -1||dfs(sign[i]))
            {
                sign[i]=x;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("E.txt","r",stdin);
    freopen("out.cpp","w",stdout);
#endif // ONLINE_JUDGE
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++){
            scanf("%d%d%d",&no[i].z,&no[i].x,&no[i].y);
            no[i].id=i;
        }
        sort(no,no+n,cmp1);
        cnx=-1;
        nx[no[0].id]=++cnx;
        for(int i=1;i<n;i++) {
            if(no[i].x==no[i-1].x) 
                nx[no[i].id]=cnx;
            else nx[no[i].id]=++cnx;
        }
        sort(no,no+n,cmp2);
        cny=-1;
        ny[no[0].id]=++cny;
        for(int i=1;i<n;i++) {
            if(no[i].y==no[i-1].y) 
                ny[no[i].id]=cny;
            else ny[no[i].id]=++cny;
        }
        cnx++;
        cny++;
        for(int i=0;i<n;i++) 
            nz[no[i].id]=no[i].z;

        clr(mp,0);clr(vx,0);clr(vy,0);
        for(int i=0;i<n;i++) {
            if(nz[i]) mp[nx[i]][ny[i]]=1;
            else{
                vx[nx[i]]=1;vy[ny[i]]=1;
            }
        }

        int flag=0;
        for(int i=0;i<cnx;i++){
            if(vx[i]){
                for(int j=0;j<cny;j++){
                    if(vy[j]) {if(mp[i][j]){flag=1;break;}}
                }
            }
            if(flag) break;
        }
        if(flag) {printf("Sorry\n");continue;}
        int ans=0;
        clr(cx,0);clr(cy,0);
        for(int i=0;i<cnx;i++){
            if(vx[i]){
                for(int j=0;j<cny;j++) if(mp[i][j]) {cy[j]=1;}
            }
        }

        for(int i=0;i<cny;i++){
            if(vy[i]){
                for(int j=0;j<cnx;j++) if(mp[j][i]) {cx[j]=1;}
            }
        }

        for(int i=0;i<cnx;i++){
            if(cx[i]) {ans++;for(int j=0;j<cny;j++) if(mp[i][j])mp[i][j]=0;}
        }
        for(int i=0;i<cny;i++){
            if(cy[i]) {
                ans++;for(int j=0;j<cnx;j++) if(mp[j][i])mp[j][i]=0;
            }
        }

        memset(sign,-1,sizeof(sign));
        for(int i=0;i<cnx;i++)
        {
            memset(vis,0,sizeof(vis));
            if(dfs(i))
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值