zoukankan      html  css  js  c++  java
  • POJ

    1、有一个n*n的矩阵,在矩阵上有k个行星,用武器射击一次可以消灭一行或者一列的行星,求消灭所有的行星的最少射击次数。

    2、最小点覆盖数 = 最大匹配数

    主要在于转化:看图:

    这样,在建成的二分图中,一条边代表一个行星,左边的一个点代表横向射击,右边的一个点代表竖向射击。

    要求最少的射击次数把所有的行星消灭,即选择最少的点,把所有的边覆盖。这不正是求最小点覆盖数吗。

    ps:

    顶点覆盖:在顶点集合中,选取一部分顶点,这些顶点能够把所有的边都覆盖了。这些点就是顶点覆盖集

    最小顶点覆盖:在所有的顶点覆盖集中,顶点数最小的那个叫最小顶点集合。

    3、

    3.1匈牙利算法(邻接矩阵):

    /*
    顶点编号从0开始的
    邻接矩阵(匈牙利算法)
    二分图匹配(匈牙利算法的DFS实现)(邻接矩阵形式)
    初始化:g[][]两边顶点的划分情况
    建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
    g没有边相连则初始化为0
    uN是匹配左边的顶点数,vN是匹配右边的顶点数
    左边是X集,右边是Y集
    调用:res=hungary();输出最大匹配数
    优点:适用于稠密图,DFS找增广路,实现简洁易于理解
    时间复杂度:O(VE)
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    
    const int MAXN=512;
    int uN,vN;//u,v 的数目,使用前面必须赋值
    int g[MAXN][MAXN];//邻接矩阵,记得初始化
    int linker[MAXN];//linker[v]=u,表示v(右边Y集合中的点)连接到u(左边X集合中的点)
    bool used[MAXN];
    bool dfs(int u){//判断以X集合中的节点u为起点的增广路径是否存在
        for(int v=0;v<vN;v++)//枚举右边Y集合中的点
            if(g[u][v]&&!used[v]){//搜索Y集合中所有与u相连的未访问点v
                used[v]=true;//访问节点v
                if(linker[v]==-1||dfs(linker[v])){//是否存在增广路径
                    //若v是未盖点(linker[v]==-1表示没有与v相连的点,即v是未盖点),找到增广路径
                    //或者存在从与v相连的匹配点linker[v]出发的增广路径
                    linker[v]=u;//设定(u,v)为匹配边,v连接到u
                    return true;//返回找到增广路径
                }
            }
            return false;
    }
    int hungary(){//返回最大匹配数(即最多的匹配边的条数)
        int res=0;//最大匹配数
        memset(linker,-1,sizeof(linker));//匹配边集初始化为空
        for(int u=0;u<uN;u++){//找X集合中的点的增广路
            memset(used,false,sizeof(used));//设Y集合中的所有节点的未访问标志
            if(dfs(u))res++;//找到增广路,匹配数(即匹配边的条数)+1
        }
        return res;
    }
    
    int main(){
        int i,ans;
        int N,K;
        int R,C;
        while(~scanf("%d%d",&N,&K)){
            uN=N;//匹配左边的顶点数
            vN=N;//匹配右边的顶点数
            memset(g,0,sizeof(g));//二分图的邻接矩阵初始化
            for(i=0;i<K;++i){
                scanf("%d%d",&R,&C);
                g[--R][--C]=1;//顶点编号从0开始的
            }
            ans=hungary();
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    ps:补一个用网络流解的,如下:

    3.2、用网络流也可以来做,边的容量设为1即可。

    用网络流的第1个模板写的,1、SAP邻接矩阵形式:

    /*
    SAP算法(矩阵形式)
    结点编号从0开始
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    
    const int MAXN=1024;
    int maze[MAXN][MAXN];
    int gap[MAXN],dis[MAXN],pre[MAXN],cur[MAXN];
    int sap(int start,int end,int nodenum){
        memset(cur,0,sizeof(cur));
        memset(dis,0,sizeof(dis));
        memset(gap,0,sizeof(gap));
        int u=pre[start]=start,maxflow=0,aug=-1;
        gap[0]=nodenum;
        while(dis[start]<nodenum){
            loop:
                for(int v=cur[u];v<nodenum;v++)
                    if(maze[u][v]&&dis[u]==dis[v]+1){
                        if(aug==-1||aug>maze[u][v])aug=maze[u][v];
                        pre[v]=u;
                        u=cur[u]=v;
                        if(v==end){
                            maxflow+=aug;
                            for(u=pre[u];v!=start;v=u,u=pre[u]){
                                maze[u][v]-=aug;
                                maze[v][u]+=aug;
                            }
                            aug=-1;
                        }
                        goto loop;
                    }
                int mindis=nodenum-1;
                for(int v=0;v<nodenum;v++)
                    if(maze[u][v]&&mindis>dis[v]){
                        cur[u]=v;
                        mindis=dis[v];
                    }
                if((--gap[dis[u]])==0)break;
                gap[dis[u]=mindis+1]++;
                u=pre[u];
        }
        return maxflow;
    }
    
    int main(){
        int i;
        int N,K;
        int R,C;
        int S,T;//超级源点,超级汇点
    
        while(~scanf("%d%d",&N,&K)){
            memset(maze,0,sizeof(maze));
            for(i=0;i<K;++i){
                scanf("%d%d",&R,&C);
                maze[R-1][N+C-1]=1;//顶点编号从0开始的
            }
            //建立超级源点
            S=N+N;
            for(int i=0;i<N;++i){
                maze[S][i]=1;
            }
            //建立超级汇点
            T=N+N+1;
            for(int i=0;i<N;++i){
                maze[N+i][T]=1;
            }
    
            printf("%d
    ",sap(S,T,N+N+2));
    
        }
        return 0;
    }
    View Code
  • 相关阅读:
    java 异常捕获机制
    读取java当前路径的文件输出在控制台
    java 简易记事本 程序
    java 利用文件输入输出流实现文件复制
    IutputStreamWriterDemo
    OutputStreamWriterDemo
    Longest Substring Without Repeat Characters
    Add Two Numbers
    Two Sum
    使用 Canvas 绘图
  • 原文地址:https://www.cnblogs.com/gongpixin/p/4978213.html
Copyright © 2011-2022 走看看