[二分图最大匹配] poj 3041 Asteroids

Asteroids
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 17609 Accepted: 9578
Description

Bessie wants to navigate her spaceship through a dangerous asteroid field in the shape of an N x N grid (1 <= N <= 500). The grid contains K asteroids (1 <= K <= 10,000), which are conveniently located at the lattice points of the grid.

Fortunately, Bessie has a powerful weapon that can vaporize all the asteroids in any given row or column of the grid with a single shot.This weapon is quite expensive, so she wishes to use it sparingly.Given the location of all the asteroids in the field, find the minimum number of shots Bessie needs to fire to eliminate all of the asteroids.
Input

  • Line 1: Two integers N and K, separated by a single space.
  • Lines 2..K+1: Each line contains two space-separated integers R and C (1 <= R, C <= N) denoting the row and column coordinates of an asteroid, respectively.
    Output

  • Line 1: The integer representing the minimum number of times Bessie must shoot.
    Sample Input

3 4
1 1
1 3
2 2
3 2
Sample Output

2
Hint

INPUT DETAILS:
The following diagram represents the data, where “X” is an asteroid and “.” is empty space:
X.X
.X.
.X.

OUTPUT DETAILS:
Bessie may fire across row 1 to destroy the asteroids at (1,1) and (1,3), and then she may fire down column 2 to destroy the asteroids at (2,2) and (3,2).

N×N 的网格中有 K 个小行星,小行星i的位置是 (Ri,Ci) 。现在有一个强力武器能够用一发光束将一整行或者一整列的小行星化为灰烬。求出最少需要几个光束能够摧毁所有的小行星。

题解:光束的攻击选择可以是横坐标从 x=1 x=N ,一共有 2N 种。攻击的顺序没有影响,所以总供给方案共有 22N 种。我们只要在这个解空间中,寻找能够摧毁所有小行星最小的解就可以了。要破坏某个小行星,只能通过对应水平方向或者竖直方向上的光束攻击,利用攻击方法只有两种这一特点,我们可以将问题按如下方法转换为图。

把光束当作图的顶点,而把小行星当作连接对应光束的边。这样转换之后,光束的攻击方案就对应一个顶点集合S,而要求攻击方案能够摧毁所有小行星,也就是图中的每条边都至少有一个属于S的端点。
这里写图片描述

如图所示,把每个小行星所对应的边都分别与一个水平方向和一个竖直方向的顶点相连,该图就成为了二分图。只要运用二分图最大匹配算法,就可以解决这个问题。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <numeric>

using namespace std;
const int MAXV = 10100;
const int MAXN = 10100;
int N,K;
int R[MAXN],C[MAXN];
int V;               //顶点数
vector<int> G[MAXV]; //图的邻接表表示
int match[MAXV];     //匹配的顶点
bool used[MAXV];     //DFS中用到的访问标记

//向图中增加一条连接u和v的边
void add_edge(int u,int v)
{
    G[u].push_back(v);
    G[v].push_back(u);
}

//通过dfs寻找增广路径
bool dfs(int v)
{
    used[v] = true;
    for(int i = 0; i < G[v].size(); i++)
    {
        int u = G[v][i], w = match[u];
        if(w < 0 || !used[w] && dfs(w))
        {
            match[v] = u;
            match[u] = v;
            return true;
        }
    }
    return false;
}

//求解二分图最大匹配
int bipartite_matching()
{
    int res = 0;
    memset(match,-1,sizeof match);
    for(int v = 0 ; v < V; v++)
    {
        if(match[v]<0)
        {
            memset(used,0,sizeof used);
            if (dfs(v))
                res++;
        }
    }
    return res;
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&N,&K);
    for(int i = 0;i<K;i++)
        scanf("%d%d",&R[i],&C[i]);

    V = N * 2;
    for(int i = 0; i < K;i++)
        add_edge(R[i]-1,N+C[i]-1);

    printf("%d\n",bipartite_matching());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值