图论常见结论

图论常见结论

二分图:

最大匹配:二分图中一个边的集合{E}中,当任意两条边都不相交于同一个节点时,这个边集被称作一个匹配,这个集合最大时,称为一个最大匹配。

最小点覆盖:如果选定一个点,看做选择了它连接的所有边,能涵盖所有边时且节点最少时,称为最小点覆盖。

最大独立集:选出一些点,使得它们两两没有边连接。选择的点最多时,这个集合称作最大独立集。

总点数=最小点覆盖+最大独立集证明

最小点覆盖=最大匹配( König定理)。从右边未匹配的点出发,沿着未匹配-匹配-未匹配去dfs。最终最小点覆盖就是左边出现的点和右边未出现的点。

最小边覆盖=点数-最大匹配。

无向图:

最大独立集=补图的最大团,反之亦然。

例题:传送门

分析,首先要考虑连续的1是没用的,其次,两个1之间的这些条件没办法同时满足。如果建模成图论,往两个点之间连边,要选出一个独立集出来。借用上面的结论,那就是求一个补图的最大团。

#include<bits/stdc++.h>

using namespace std;

const int N = 45;

int nd, nm, G[N][N];
int cntClique, pts[N], res[N], cnt[N];

map<string, int> H;

int n;

vector<int> v;

bool dfs(int pos, int num){
    for(int i=pos+1;i<=n;++i){
        if(cnt[i]+num<=cntClique)return false;//这里是一处剪枝

        if(G[pos][i]){//考虑与当前团节点的关系
            int ok=1;
            for(int id=1;id<=num;++id){
                if(!G[i][pts[id]]){
                    ok=0;break;
                }
            }

            if(ok){
                pts[num+1]=i;
                if(dfs(i,num+1))return true;
            }
        }
    }

    if(num>cntClique){ //多枚举一个,最多只扩充一个点
        for(int i=1;i<=num;++i){
            res[i]=pts[i];
        }
        cntClique=num;
        return true;
    }

    return false;
}

void maxClique(){
    cntClique=-1;
    for(int i=n;i>0;--i){
        pts[1]=i;
        dfs(i,1);
        cnt[i]=cntClique;
    }
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    memset(G,1,sizeof G);

    cin>>nd>>nm;
    for(int i=1;i<=nd;++i){
        int op;string s;
        cin>>op;
        if(op==1){
            v.clear();
        }else{
            cin>>s;
            if(!H.count(s)){
                H[s]=++n;
            }
            /*n^2建图跑的飞起,cf数据有问题*/
            int y=H[s];
            for(auto& x:v){
                G[x][y]=0;
                G[y][x]=0;
            }
            v.push_back(y);
        }
    }

    maxClique();
    if(cntClique<0){
        cout<<"0";
    }else{
        cout<<cntClique;
    }
    return 0;
}

posted @ 2020-08-28 13:02  John_Ran  阅读(282)  评论(0编辑  收藏  举报