牛客练习赛30E(国政议事)

链接:https://ac.nowcoder.com/acm/contest/216/E
思路:关于二分图必须匹配问题,看这里,引用一下:https://blog.csdn.net/element_hero/article/details/82845931
事实上对于匈牙利算法中不完备匹配我们可能需要额外建图(还没怎么想懂后面我用匈牙利算法再写一遍),事实上用网络流的话不需要重新建图,只需要在残留网络上跑一边tarjan边双即可,但注意要思考清楚跑tarjan的时候哪些边可以走,我一开始就是随便抄了一下一直不对,网上也没人用我这种图的表示方法,后面我认真思考了一下,对于u,v有流量,那么反向边可以跑而正向边不能跑,如果u,v没流量那么正向边可以跑而反向边不能跑,其实根本不用重新建图,概括起来也就一句话e.cap-e.flow>0时可以跑,否则不行。最后对于所有已经匹配的边检查一下是否属于一个强连通分量,如果属于则是可行边,否则是必须边。
代码:

#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn = 1001;
const int INF = 1e9;

struct edge{
    int from,to,cap,flow;
};

    int n,m,s,t,q;
    vector<edge> edges;
    vector<int> G[maxn],RG[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];

    vector<int> bcc[maxn];//强连通分量
    int dfn[maxn];
    int low[maxn];
    int scount[maxn];//是否为割点
    int sccno[maxn];//属于哪一个强连通分量
    int ntime;
    int bcc_cnt;
    deque<int> P;

    void init(){
        edges.clear();
        for(int i=0;i<=n;i++)G[i].clear();
    }

    void addedge(int from,int to,int cap){
        edges.push_back(edge{from,to,cap,0});
        edges.push_back(edge{to,from,0,0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool bfs(){
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!q.empty()){
            int x = q.front();
            q.pop();
            for(int i=0;i<G[x].size();i++){
                edge &e = edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int dfs(int x,int a){
        if(x==t||a==0)return a;
        int flow = 0,f;
        for(int &i = cur[x];i<G[x].size();i++){
            edge &e = edges[G[x][i]];
            if(d[x] + 1 == d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow -=f;
                flow+=f;
                a-=f;
                if(a==0)break;
            }
        }
        return flow;
    }

    int maxflow(){
        int flow = 0;
        while(bfs()){
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }

 void tarjan(int u){
    dfn[u] = low[u] = ++ntime;
    P.push_back(u);
    for(int i=0;i<G[u].size();i++){
        edge &e = edges[G[u][i]];
        if(e.cap-e.flow==0)continue;//一定要注意这里的判断!!!!因为写法不同所以这里判断的写法也不同!!!
        int v = e.to;
        if(!dfn[v]){
        tarjan(v);
        low[u] = min(low[u],low[v]);
        }
        else if(!sccno[v]){
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        bcc_cnt++;
        int tmp;
        do{
            tmp = P.back();
            P.pop_back();
            sccno[tmp] = bcc_cnt;
            scount[bcc_cnt]++;//计算连通块所含的点数
        }while(tmp!=u);
    }
}

void find_bcc(){
    memset(sccno,0,sizeof(sccno));
    memset(dfn,0,sizeof(dfn));
    memset(scount,0,sizeof(scount));
    ntime = bcc_cnt = 0;
    for(int i=0;i<=2*n+1;++i)if(!dfn[i])tarjan(i);
}

int main(){
    scanf("%d%d",&n,&q);
    s = 0;
    t = 2*n+1;
    init();
    for(int i=0;i<q;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        addedge(a,b+n,1);
    }
    for(int i=1;i<=n;i++)addedge(s,i,1),addedge(i+n,t,1);
    int res = maxflow();
    printf("%d ",res);
    find_bcc();
    vector<int> ans;
    for(int i=0;i<q*2;i+=2){
        edge e = edges[i];
        if(e.flow&&sccno[e.from]!=sccno[e.to])ans.push_back(i/2+1);
    }
    sort(ans.begin(),ans.end());
    printf("%d\n",ans.size());
    for(int i=0;i<ans.size();i++){
        printf("%d\n",ans[i]);
    }   
    return 0;
}

补充一下删边的做法,对于匹配边我们删除之后再跑匈牙利算法,如果答案不变就不是必须边,否则就是必须边。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2000;

//注意编号从1开始
vector<int> G[maxn],RG[maxn];
bool vis[maxn];
int link[maxn];
int relink[maxn];
int m,s,t,q;
int nx,ny;
int dfn[maxn];
int low[maxn];
int sccno[maxn];
int scount[maxn];
int ntime;
int bcc_cnt;//连通块数量
int nowi = -1;
deque<int> P;

    struct edge{
        int from,to;
    };

    vector<edge> edges;

    void init(int nx,int ny){
        for(int i=0;i<=nx+ny+2;i++)G[i].clear();
    }

    inline void addedge(int from,int to){
        edges.push_back(edge{from,to});
        q = edges.size();
        G[from].push_back(q-1);
    }

    bool dfs(int u){
        vis[u] = 1;
        for(int i=0;i<G[u].size();i++){
            if(G[u][i]==nowi)continue;
            edge e = edges[G[u][i]];
            int v = e.to;
            if(!vis[v]){
                vis[v] = 1;
                if(link[v]==-1||dfs(link[v])){
                    link[v] = u;
                    return true;
                }
            }
        }
        return false;
    }

    int hungrey(){
        int res = 0;
        memset(link,-1,sizeof(link));
        for(int i=1;i<=nx;i++){
            memset(vis,0,sizeof(vis));
            if(dfs(i))res++;
        }
        return res;
    }

int main(){
    scanf("%d%d",&nx,&m);
    ny = nx;
    init(nx,ny);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        v+=nx;
        addedge(u,v);
    }
    int res = hungrey();
    for(int i=nx+1;i<=nx+ny;i++){
        relink[i] = link[i];  
    }
    printf("%d ",res);
    vector<int> ans;
    for(int i=0;i<m;i++){
        nowi = i;
        int u = edges[i].from;
        int v = edges[i].to;
        if(relink[v]!=u)continue;
        int now = hungrey();
        if(now!=res)ans.push_back(i+1);
    }
    printf("%d\n",ans.size());
    for(int i=0;i<ans.size();i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容