A-L 二分匹配 M-O 二分图多重匹配 P-Q 二分图最大权匹配 R-S 一般图匹配带花树 模板请自己找
   
ID
Origin
Title
  61 / 72 Problem A HDU 1045 Fire Net
  52 / 112 Problem B HDU 2444 The Accomodation of Students
  45 / 86 Problem C HDU 1083 Courses
  44 / 63 Problem D HDU 1281 棋盘游戏
  35 / 85 Problem E HDU 2819 Swap
  29 / 110 Problem F HDU 2389 Rain on your Parade
  29 / 77 Problem G HDU 4185 Oil Skimming
  27 / 39 Problem H POJ 3020 Antenna Placement
  28 / 44 Problem I HDU 1054 Strategic Game
  25 / 36 Problem J HDU 1151 Air Raid
  26 / 64 Problem K POJ 2594 Treasure Exploration
  26 / 61 Problem L HDU 3829 Cat VS Dog
  23 / 56 Problem M POJ 2289 Jamie's Contact Groups
  15 / 74 Problem N POJ 2112 Optimal Milking
  14 / 31 Problem O POJ 3189 Steady Cow Assignment
  29 / 56 Problem P HDU 2255 奔小康赚大钱
  20 / 33 Problem Q HDU 3488 Tour
  10 / 20 Problem R URAL 1099 Work Scheduling
  7 / 22 Problem S HDU 4687 Boke and Tsukkomi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


61 / 72 Problem A HDU 1045 Fire Net

s.二分匹配或者暴搜

 

52 / 112 Problem B HDU 2444 The Accomodation of Students

d.有一些学生,他们之间有些互相认识,有些不认识,其中若A和B认识,B和C认识,并不代表A和C认识,现在提供互相认识的学生编号,问是否能将这些学生分成两组,同一组的学生互相不认识,如果可以,则分配给这些学生双人间,只有是朋友才能住双人间,问最多要准备几间房

即判断是不是二分图,若是,其最大匹配数是多少?

s.首先用交叉染色法判断是不是二分图,然后用匈牙利算法求最大匹配。

c.唯一比较坑的是边数开了1w,提交G++超时!而用C++却Runtime Error,才知道可能是数组原因。。

#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;

#define MAXN 205//点数
#define MAXM 41000//边数。不要吝啬内存,只要不超限。一定要开够

int color[MAXN];
int n;

struct Edge{
    int to,next;
}edge[MAXM];

int head[MAXN];
int tot;

void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}

bool bfs(int start){//bfs交叉染色,两种颜色标记为 1 和 -1,未染色标记为 0
    int v;
    queue<int>q;
    color[start]=1;
    q.push(start);

    while(!q.empty()){
        int temp=q.front();
        q.pop();

        for(int i=head[temp];i!=-1;i=edge[i].next){//

            v=edge[i].to;

            if(color[v]==0){//未染色
                if(color[temp]==1){
                    color[v]=-1;
                }
                else{
                    color[v]=1;
                }
                q.push(v);
            }
            else{//已染色
                if(color[v]==color[temp]){//相邻的两点颜色相同
                    return false;//不能交叉染色
                }
            }

        }
    }
    return true;
}

//匈牙利算法
int linker[MAXN];
bool used[MAXN];

bool dfs(int u){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(!used[v]){
            used[v]=true;
            if(linker[v]==-1||dfs(linker[v])){
                linker[v]=u;
                return true;
            }
        }
    }
    return false;
}
int hungary(){
    int res=0;
    memset(linker,-1,sizeof(linker));
    //点的编号0~uN-1
    for(int u=0;u<n;u++){
        if(color[u]==-1)continue;//只看颜色是1的集合

        memset(used,false,sizeof(used));
        if(dfs(u))res++;
    }
    return res;
}

int main(){

    int m;
    int u,v;
    bool flag;

    while(~scanf("%d",&n)){

        memset(color,0,sizeof(color));
        init();

        scanf("%d",&m);

        for(int i=0;i<m;++i){
            scanf("%d%d",&u,&v);

            addedge(u-1,v-1);
            addedge(v-1,u-1);
        }

        flag=true;

        for(int i=0;i<n;++i){
            if(color[i]!=0)continue;//已经染色
            if(!bfs(i)){//不是二分图
                flag=false;
                break;
            }
        }

        if(flag){
            printf("%d\n",hungary());
        }
        else{
            printf("No\n");
        }
    }

    return 0;
}
View Code

c2.这里的程序hungary()用vector实现,同时染色用的dfs

/*
HDU 2444 The Accomodation of Students

*/
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
#define MAXN 202
vector<int>EV[MAXN];
int linker[MAXN];
bool used[MAXN];
int uN;
int matchs[MAXN],cnt[MAXN];
bool dfs(int u)
{
    int i;
    for(i=0;i<EV[u].size();i++)
    {
        int v=EV[u][i];
        if(!used[v])
        {
            used[v]=true;
            if(linker[v]==-1||dfs(linker[v]))
            {
                linker[v]=u;
                return true;
            }    
        }    
    }
    return false;    
}   
int hungary()
{
    int res=0;
    int u;
    memset(linker,-1,sizeof(linker));
    for(u=1;u<=uN;u++)
    {
        memset(used,false,sizeof(used));
        if(dfs(u))  res++;
    }
    return res;    
}  
bool judge(int x,int y)
{
    int i;
    for(i=0;i<EV[x].size();i++)
    {
        if(cnt[EV[x][i]]==0)
        {
            cnt[EV[x][i]]=-1*y;
            matchs[EV[x][i]]=true;
            if(!judge(EV[x][i],-1*y)) return false;
        }   
        else if(cnt[EV[x][i]]==y)  return false; 
    }    
    return true;
}    
bool matched()
{
    int i;
    memset(matchs,false,sizeof(matchs));
    for(i=1;i<=uN;i++)
    {
        if(EV[i].size()&&!matchs[i])
        {
            memset(cnt,0,sizeof(cnt));
            cnt[i]=-1;
            matchs[i]=true;
            if(!judge(i,-1)) return false;
        }    
    }
    return true;    
}     
int main()
{
    int m;
    int i;
    int u,v;
    while(scanf("%d%d",&uN,&m)!=EOF)
    {
        for(i=1;i<=uN;i++)
          if(EV[i].size())  EV[i].clear();
        while(m--)
        {
            scanf("%d%d",&u,&v);
            EV[u].push_back(v);
            EV[v].push_back(u);
        }    
     
         if(matched())
                  printf("%d\n",hungary()/2);
         else  printf("No\n");    
    } 
    return 0;
}
View Code

 

45 / 86 Problem C HDU 1083 Courses

s.最大匹配


44 / 63 Problem D HDU 1281 棋盘游戏

s.二分匹配


35 / 85 Problem E HDU 2819 Swap

s.二分匹配,记录过程


29 / 110 Problem F HDU 2389 Rain on your Parade

s.先用匈牙利算法的DFS版本,一直超时,

后改用Hopcroft-Carp算法,顺利AC。


29 / 77 Problem G HDU 4185 Oil Skimming

s.二分匹配


27 / 39 Problem H POJ 3020 Antenna Placement

s.二分匹配,主要是建图


28 / 44 Problem I HDU 1054 Strategic Game

此题就是二分图的最小点覆盖。
点比较多,1500个。普通的匈牙利算法会超时的。可以用邻接表实现匈牙利算法。
但是我启用二分匹配中的战斗机:Hopcroft-Carp的算法。专门处理点比较多的情况。
 
好像也可以用树形DP做。等下来学习下。


25 / 36 Problem J HDU 1151 Air Raid

d.一个有向无环图(DAG),M个点,K条有向边,求DAG的最小路径覆盖数

s.DAG的最小路径覆盖数=DAG图中的点数-相应二分图中的最大匹配数

/*
顶点编号从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 K,M;
    int u,v;
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&M,&K);
        uN=M;//匹配左边的顶点数
        vN=M;//匹配右边的顶点数
        memset(g,0,sizeof(g));//二分图的邻接矩阵初始化
        for(i=0;i<K;++i){
            scanf("%d%d",&u,&v);
            g[--u][--v]=1;//顶点编号从0开始的
        }
        ans=M-hungary();
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

26 / 64 Problem K POJ 2594 Treasure Exploration

此题跟普通的路径覆盖有点不同。
You should notice that the roads of two different robots may contain some same point.
也就是不同的路径可以有重复点。
先用floyed求闭包,就是把间接相连的点也连起来。


26 / 61 Problem L HDU 3829 Cat VS Dog

最大独立集


23 / 56 Problem M POJ 2289 Jamie's Contact Groups

多重匹配+二分

貌似也可以用最大流+二分做

d.Jamie有很多联系人,但是很不方便管理,他想把这些联系人分成组,已知这些联系人可以被分到哪个组中去,而且要求每个组的联系人上限最小,即有一整数k,使每个组的联系人数都不大于k,问这个k最小是多少?

s.一对多的二分图的多重匹配。二分图的多重匹配算法的实现类似于匈牙利算法,对于集合x中的元素xi,找到一个与其相连的元素yi后,检查匈牙利算法的两个条件是否成立,若yi未被匹配,则将

xi,yi匹配。否则,如果与yi匹配的元素已经达到上限,那么在所有与yi匹配的元素中选择一个元素,检查是否能找到一条增广路径,如果能,则让出位置,让xi与yi匹配。

二分求出limit,知道找到可以构成多重匹配的最小限制limit,在main函数中二分搜索。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define N 1010
int vis[N], maps[N][N], ans, n, m;
struct node
{
    int cnt;///和yi相匹配的个数;
    int k[N];///和yi相匹配的x的集合;
}Linky[N];

bool Find(int u, int limit)
{
    for(int i=1; i<=m; i++)
    {
        if(!vis[i] && maps[u][i])
        {
            vis[i] = 1;
            if(Linky[i].cnt < limit)
            {
                Linky[i].k[ Linky[i].cnt++ ] = u;
                return true;
            }
            for(int j=0; j<Linky[i].cnt; j++)
            {
                if(Find( Linky[i].k[j], limit ))
                {
                    Linky[i].k[j] = u;
                    return true;
                }
            }
        }
    }
    return false;
}

bool xyl(int limit)///匈牙利算法;
{
    memset(Linky, 0, sizeof(Linky));
    for(int i=1; i<=n; i++)
    {
        memset(vis, 0, sizeof(vis));
        if(!Find(i, limit))///当前的limit让i没有匹配,所以不能用limit;
            return false;
    }
    return true;
}

int main()
{
    int x;
    char s[20], ch;
    while(scanf("%d %d", &n, &m), m+n)
    {
        memset(maps, 0, sizeof(maps));
        for(int i=1; i<=n; i++)
        {
            scanf("%s", s);
            while(1)
            {
                scanf("%d%c", &x, &ch);
                maps[i][x+1] = 1;
                if(ch == '\n')
                    break;
            }
        }
        int L = 1, R = n;
        ans = n;
        while(L <= R)
        {
            int mid = (L+R)/2;
            if(xyl(mid))///如果当前mid满足题意;
            {
                R = mid-1;
                ans = mid;
            }
            else
                L = mid+1;
        }
        printf("%d\n", ans);
    }
    return 0;
}
View Code

相关文章: