• 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;
        }
    }

    差分约束系统

    最小化最长路

    例题:糖果

  • 相关阅读:
    第三节课: Python 基本数据类型讲解(1/3)
    第二节课 虚拟机安装
    Java生成带LOGO的二维码
    Oracle中存储图片的类型为BLOB类型,Java中如何将其读取并转为字符串?
    简单分析下mybatis中mapper文件中小知识
    Oracle中,如何将String插入到BLOB类型字段
    Oracle + Mybatis批量插入数据,xml.mapper种的写法
    java中拼接两个对象集合
    关于spring boot在IDE工具中可以启动成功,但是打成jar包以及运行jar包失败的问题
    springboot整合mybatis之注解方式
  • 原文地址:https://www.cnblogs.com/lcezych/p/11196583.html
Copyright © 2020-2023  润新知