牛客练习赛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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容