【二分图匹配模板题:匈牙利算法或者最大流】 POJ - 3041 Asteroids

Asteroids  POJ - 3041 

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).

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int maxn=2e4+5;
vector <int> G[maxn];  //图的邻接表表示
int match[maxn];       //所匹配的顶点
int V;                 //顶点数
bool used[maxn];       //dfs中所用到的访问标记

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

bool dfs(int v)//通过dfs找到增广路
{
    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()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++) G[i].clear();
        V=2*n;
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add_edge(x,y+500);
        }
        printf("%d\n",bipartite_matching());
    }
    return 0;
}

或者用Dinic

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAX_V=60001;
const int st=0;
const int en=60000;
const int INF=0x3f3f3f3f;

struct edge
{
    int to,cap,rev; //用于表示边的结构体(终点,容量,反向边)
};

vector <edge> G[MAX_V]; //图的邻接表表示
int level[MAX_V];   //顶点到源点的距离标号
int iter[MAX_V];    //当前弧,在其之前的边已经没有用了
char t[222];

void add_edge(int from,int to,int cap)
{
    G[from].push_back((edge){to,cap,G[to].size()});
    G[to].push_back((edge){from,0,G[from].size()-1});
}

void bfs(int s)  //通过bfs计算从源点出发的距离标号
{
    memset(level,-1,sizeof(level));
    queue <int> q;
    level[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int v=q.front();q.pop();
        for(int i=0;i<G[v].size();i++)
        {
            edge &e=G[v][i];
            if(e.cap>0&&level[e.to]<0)
            {
                level[e.to]=level[v]+1;
                q.push(e.to);
            }
        }
    }
}

int dfs(int v,int t,int f)  //通过dfs寻找增广路
{
    if(v==t) return f;
    for(int &i=iter[v];i<G[v].size();i++)
    {
        edge &e=G[v][i];
        if(e.cap>0&&level[v]<level[e.to])
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s,int t)
{
    int flow=0;
    for(;;)
    {
        bfs(s); //计算层次图
        if(level[t]<0) return flow; //找不到s-t路径
        memset(iter,0,sizeof(iter)); //初始化当前弧
        int f;
        while((f=dfs(s,t,INF))>0)  //更新最大流
            flow+=f;
    }
    return flow;
}

int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=1;i<=k;i++)
        {
            add_edge(i,i+10000,1); //x轴
            add_edge(i+40000,i+50000,1); //y轴
            add_edge(i+20000,i+30000,1); //自身
            add_edge(st,i,1);
            add_edge(i+50000,en,1);
        }
        for(int i=1;i<=k;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add_edge(x+10000,i+20000,1);
            add_edge(i+30000,y+40000,1);
        }
        printf("%d\n",max_flow(st,en));
    }
    return 0;
}

匈牙利算法

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=505;
int G[maxn][maxn],vis[maxn],col[maxn];
int n,m,k;

int dfs(int u)
{
    for(int i=1;i<=n;i++)
    {
        if(G[u][i]&&!vis[i])
        {
            vis[i]=1;
            if(!col[i]||dfs(col[i]))
            {
                col[i]=u;
                return 1;
            }
        }
    }
    return 0;
}

int maxmatch()
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ans++;
    }
    return ans;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(G,0,sizeof(G));
        memset(col,0,sizeof(col));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v]=1;
        }
        printf("%d\n",maxmatch());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41037114/article/details/81503939