1. 程式人生 > >訓練指南 UVA - 11419(二分圖最小覆蓋數)

訓練指南 UVA - 11419(二分圖最小覆蓋數)

tps int type bbbb bbb http for solver min


layout: post
title: 訓練指南 UVA - 11419(二分圖最小覆蓋數)
author: "luowentaoaa"
catalog: true
mathjax: true
tags:
- 二分圖
- 最小點覆蓋
- 圖論
- 訓練指南


SAM I AM

UVA - 11419

題目大意:給出一個R×C的網格,網格上棉紡了一些目標。可以在網格外發射子彈,子彈會沿著垂直或水平方向飛行,並且打掉飛行路徑上的所有目標。你的任務是計算出最少需要多少子彈,各從哪個位置發射,才能把所有目標全部打掉。
解題思路:K?nig定理:最小覆蓋數等於最大匹配數。把目標所在的坐標,轉化為XY結點,行看成X結點,列看成Y結點。那現在問題就變成了,如何選最少的結點,覆蓋所有的邊。

求最小覆蓋的步驟大致如下:1)在右邊找到一個未被匹配過的點,標記。2)走一條沒被匹配過的邊,到左邊的點,標記。3)走一條匹配過的邊到右邊,標記。4)重復2,3步驟直到不能再走。5)回到步驟一,直到找不到未被匹配且未被標記的右邊的點。6)標記結束後,右邊沒有標記的點,和左邊標記過的點,就可以覆蓋所有的邊。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=1e3+50;
const ll inf=1e10;
const ll INF = 1000000000;
const double eps=1e-5;
#define bug cout<<"bbibibibbbb="<<endl;
/// 二分圖最大基數匹配
struct BPM{
    int n,m;    /// 左右頂點個數
    vector<int>G[maxn]; /// 鄰接表
    int left[maxn];    /// left[i]為右邊第i個點的匹配點編號,-1表示不存在
    bool T[maxn];       /// T[i]為右邊第i個點是否已標記

    int right[maxn];        /// 求最小覆蓋用
    bool S[maxn];           /// 求最小覆蓋用

    void init(int n,int m){
        this->n=n;
        this->m=m;
        for(int i=0;i<n;i++)G[i].clear();
    }

    void AddEdge(int u,int v){
        G[u].push_back(v);
    }

    bool match(int u){
        S[u]=true;
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i];
            if(!T[v]){
                T[v]=true;
                if(left[v]==-1||match(left[v])){
                    left[v]=u;
                    right[u]=v;
                    return true;
                }
            }
        }
        return false;
    }
    /// 求最大匹配
    int solve(){
        memset(left,-1,sizeof(left));
        memset(right,-1,sizeof(right));
        int ans=0;
        for(int u=0;u<n;u++){
            memset(S,0,sizeof(S));
            memset(T,0,sizeof(T));
            if(match(u))ans++;
        }
        return ans;
    }
    /// 求最小覆蓋。X和Y為最小覆蓋中的點集
    int mincover(vector<int>& X,vector<int>& Y){
        int ans=solve();
        memset(S,0,sizeof(S));
        memset(T,0,sizeof(T));
        for(int u=0;u<n;u++)
            if(right[u]==-1)match(u);
        for(int u=0;u<n;u++)
            if(!S[u])X.push_back(u);
        for(int v=0;v<n;v++)
            if(T[v])Y.push_back(v);
        return ans;
    }
};
BPM solver;
int R,C,N;
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int kase=0;
    while(cin>>R>>C>>N&&R&&C&&N){
        solver.init(R,C);
        for(int i=0;i<N;i++){
            int r,c;
            cin>>r>>c;r--;c--;
            solver.AddEdge(r,c);
        }
        vector<int>X,Y;
        int ans=solver.mincover(X,Y);
        cout<<ans;
        for(int i=0;i<X.size();i++)cout<<" r"<<X[i]+1;
        for(int j=0;j<Y.size();j++)cout<<" c"<<Y[j]+1;
        cout<<endl;
    }
    return 0;
}

訓練指南 UVA - 11419(二分圖最小覆蓋數)