zoukankan      html  css  js  c++  java
  • qbzt day4 下午

    有向图的强连通分量

    强联通:两个点之间可以互相到达

    如果某个图任意两个点都是强联通的,那么称这个图强联通

    如果一个图的子图是强联通的,那么称这个图是强联通子图

    一个图的极大强联通子图被称作强连通分量

    有强联通分量意味着环

     

    例:受欢迎的牛

    如果有环,意味着这个环里的牛都互相喜欢

    我们可以先求出环,然后把每一个环都看作一个点,这样整个图就变成了一个DAG(有向无环图)

    看有几个点出度为0,如果大于一个点没有出边,就说明没有最受欢迎的牛

    如果只有一个,那么强联通分量的大小就是答案

    void tarjan(int u){
        dfn[u]=++ind;
        low[u]=dfn[u];
        s[top++]=u;
        in[u]=1;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(dfn[v]==0){//mei bian li dao, v zai zi shu li mian
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }else{//bian li dao le, v bu zai zi shu li mian
                if(in[v]){//zai zhan li mian
                    low[u]=min(low[u],dfn[v]);
                }
            }
        }
        if(dfn[u]==low[u]){//fa xian scc
            cnt_scc++;
            while(s[top]!=u){//bu duan chu zhan
                top--;
                in[s[top]]=0;
                scc[s[top]]=cnt_scc;
            }
        }
    }

     完整代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m;
    
    int cnt,head[50000];
    
    struct edge
    {
        int to,nxt;
    }edg[50005];
    
    inline void add(int from,int to)
    {
        edg[++cnt].to=to;
        edg[cnt].nxt=head[from];
        head[from]=cnt;
    }
    
    int dfn[50005],low[50005],ind,in[50005];
    int s[50005],top;
    int cnt_scc;
    int scc[50005],cntscc[50005];
    
    void tarjan(int x)
    {
        dfn[x]=++ind;
        low[x]=dfn[x];
        s[top++]=x;
        in[x]=1;
        for(int i=head[x];i;i=edg[i].nxt)
        {
            int v=edg[i].to;
            if(!dfn[v])
            {
                tarjan(v);
                low[x]=min(low[x],low[v]);
            }
            else
            {
                if(in[v])
                {
                    low[x]=min(low[x],dfn[v]);
                }
            }
        }
        if(dfn[x]==low[x])
        {
            cnt_scc++;
            while(s[top]!=x)
            {
                top--;
                in[s[top]]=0;
                scc[s[top]]=cnt_scc;
                cntscc[cnt_scc]++;
            }
        }
    }
    
    int out[50005];
    int ans;
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        for(int i=1;i<=n;i++)
        {
            if(!dfn[i]) tarjan(i);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];j;j=edg[j].nxt)
            {
                int k=edg[j].to;
                if(scc[i]!=scc[k]) out[scc[i]]++;
            }
        }
        for(int i=1;i<=cnt_scc;i++)
        {
            if(!out[i]) 
            {
                if(!ans)
                    ans=i;
                else
                {
                    cout<<0;
                    return 0;
                }
            }
        }
        cout<<cntscc[ans];
    }

     

    求强联通分量:

    首先想到dfs,我们不妨先看看dfs数

    发现有向图中dfs树会有横叉边

    显然的事实:一个强连通分量一定是dfs树上的连续的一块

    我们定义两个数组

    dfn[x]表示x是第几个被dfs到的数(时间戳)

    low[x]表示当前节点以及他的子树所有出边所能连到的dfn值中最小的一个

     

    如果某一个点的low和他的dfn相同,就意味着出现强联通分量,就把这个强连通分量拿去(没了)

    用一个栈来实现,寻找low时只在栈里面找,弹出时不断从栈顶弹出直到弹出这个点

     

    A gift

    把g按照升序排序,枚举g0,跑最小生成树(然后就tle)O(m^2logm)

    当我们再连一条新边时,会出现环,所以找到环上的最大的边然后删掉它,复杂度O(n)

    总复杂度O(mn)

     

    狼抓兔子

    平面图的最小割等于对偶图的最短路

    最小割:所有割中边权和最小的割

    对偶图:边变成点,点变成边    平面图的每一个块变成一个点,每一条边变成垂直的一条边

    #include<cstdio>
    #include<cstring>
    #define oo 0x3f
    #define MAXN 2000001
    using namespace std;
    struct edge {
        int v,to,next;
    } e[MAXN*2];
    int dis[MAXN],q[MAXN],head[MAXN];
    bool tag[MAXN];
    int n,m,ne,x;
    void insert(int u,int v,int w) {
        ne++;
        e[ne].to=v;
        e[ne].next=head[u];
        e[ne].v=w;
        head[u]=ne;
    }
    void spfa() {
        memset(dis,oo,sizeof(dis));
        int t=0,w=1;
        tag[0]=1;
        q[w]=0;
        dis[0]=0;
        while(t!=w) {
            int u=q[t++];
            tag[u]=0;
            if(t==MAXN)    t=0;
            for(int i=head[u]; i; i=e[i].next) {
                int v=e[i].to;
                if(dis[v]>dis[u]+e[i].v) {
                    dis[v]=dis[u]+e[i].v;
                    if(tag[v]==0) {
                        q[w++]=v;
                        tag[v]=1;
                        if(w==MAXN)    w=0;
                    }
                }
            }
        }
    }
    int main() {
        scanf("%d%d",&n,&m);
        int nm=(n*m-n-m+1)<<1;
        for(int i=1; i<=n; i++) {
            for(int j=1; j<m; j++) {
                scanf("%d",&x);
                if(i==1)    insert(j,nm+1,x);
                else if(i==n)    insert(0,(((n-1)<<1)-1)*(m-1)+j,x);
                else    insert(((i-1)<<1)*(m-1)+j,(((i-1)<<1)-1)*(m-1)+j,x);
            }
        }
        for(int i=1; i<n; i++) {
            for(int j=1; j<=m; j++) {
                scanf("%d",&x);
                if(j==1)    insert(0,((i<<1)-1)*(m-1)+1,x);
                else if(j==m)    insert(((i<<1)-1)*(m-1),nm+1,x);
                else    insert(((i-1)<<1)*(m-1)+j-1,((i<<1)-1)*(m-1)+j,x);
            }
        }
        for(int i=1; i<n; i++) {
            for(int j=1; j<m; j++) {
                scanf("%d",&x);
                insert((((i-1)<<1)+1)*(m-1)+j,((i-1)<<1)*(m-1)+j,x);
            }
        }
        spfa();
        printf("%d
    ",dis[nm+1]);
        return 0;
    }

     

    奶牛的旅行

    0/1分数规划

    二分答案+spfa判负环

     

    最优比率生成树

     

    抢掠计划

    把强联通分量缩点

    从点1开始跑一遍最长路spfa,在酒吧取max

     

    奶牛接力跑

    倍增floyd(floyd快速幂)

    g[1][i][j]表示从i到j只经过一条边的最短路

    如何转移到g[2][i][j]?

    枚 举所有中点k,对于所有的k,g[2][i][j]表示min(g[1][i][k]+g[1][k][j])

    同理也可以求出g[4][i][k]

    所以g[p][i][j]=min(g[p/2][i][k]+g[p/2][k][j])

    g[3][i][j]=min(g[2][i][k]+g[1][k][j])

    和快速幂的关系:用类似于快速幂的方法将其分解

    while(b){
        if(b&1){
            memset(f,0x3f,sizeof(f));
            for(int k=1;k<=n;k++){
                for(int i=1;i<=n;i++){
                    for(int j=1;j<=n;j++){
                        f[i][j]=min(f[i][j],ret[i][k]+g[k][j]);
                    }
                }
            }
            memcpy(ret,f,sizeof(f));
        }
        memset(f,0x3f,sizeof(f));
        for(int k=1;k<=n;k++){
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    f[i][j]=min(f[i][j],g[i][k]+g[k][j]);
                }
            }
        }
        memcpy(g,f,sizeof(f));
       b>>=1; } print(ret[S][E])

     

    Destroying road

    1. 两条最短路不交叉  删掉的边的数量:m-dis[s1][t1]-dis[s1][t2]
    2. 两条最短路有公共部分

    Bfs求最短路

     

    匈牙利算法:配对

    int g[N][N];
    int lk[N];// mei zi xi huan na ge nan de
    bool vis[N];//zhe yi lun, mei zi you mei you bei jiao huan
    
    bool find(int x){
        for(int i=1;i<=n;i++){
            if(!vis[i]&&g[x][i]){
                vis[i]=1;
                if(lk[i]==0||find(lk[i])){
                    lk[i]=x;
                    return 1;
                }
            }
        }
        return 0;
    }
    
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        if(find(i)){
            hunpei++;
        }else{
            break;
        }
    }

    差分约束系统

    最小化最长路

    例题:糖果

  • 相关阅读:
    net core 使用 rabbitmq
    asp.net core WebApi 返回 HttpResponseMessage
    asp.net core 2.1 WebApi 快速入门
    JQuery EasyUI combobox动态添加option
    php截取字符去掉最后一个字符
    JQuery EasyUI Combobox的onChange事件
    对于不返回任何键列信息的 selectcommand 不支持 updatecommand 的动态 sql 生成
    Access2007 操作或事件已被禁用模式阻止解决办法
    Easyui 中 Tabsr的常用方法
    Win 7 IE11不能下载文件,右键另存为也不行
  • 原文地址:https://www.cnblogs.com/lcezych/p/11196583.html
Copyright © 2011-2022 走看看