图论复习(各种习题)

poj——3041    Asteroids

​新疆

HDU——2444 The Accomodation of Students

题材:一些学生之间是情侣关系(关系不能够传递),问能或不能够将学生分成两堆使得一样堆的学习者之间不是朋友。假若不能输出“No”,能够的话输出最多可以分出几对小盆友。

题解:先二分图染色判断其是或不是是二分图,然后在跑最大匹配

图片 1图片 2

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 510
using namespace std;
bool flag,vis[N];
int n,m,x,y,tot,ans,col[N],girl[N],head[N],map[N][N];
queue<int>q;
struct Edge
{
    int from,to,next;
}edge[N*N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int find(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]&&map[x][i])
        {
            vis[i]=true;
            if(girl[i]==-1||find(girl[i])){girl[i]=x; return 1;}
        }
    }
    return 0;
}
int color(int s)
{
    queue<int>q;
    q.push(s); col[s]=0;
    while(!q.empty())
    {
        int x=q.front();
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            if(col[t]!=-1){if(col[t]==col[x]) {flag=true; return 1;}}
            else
            {
                col[t]=col[x]^1;
                q.push(t);
            }
        }
        q.pop();
    }
    return 0;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        ans=0;flag=false;tot=0;
        memset(map,0,sizeof(map));
        memset(col,-1,sizeof(col));
        memset(edge,0,sizeof(edge));
        memset(head,0,sizeof(head));
        for(int i=1;i<=m;i++)
        {
            x=read(),y=read();
            map[x][y]=1;
            add(x,y),add(y,x);
        }
        for(int i=1;i<=n;i++)
         if(col[i]==-1)
         {
             if(color(i)) break;
          } 
        if(flag) {printf("No\n"); continue;}
        memset(girl,-1,sizeof(girl));
        for(int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            if(find(i)) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

二分图染色+最大匹配数

成都

         ⑤ 、拓扑排序

一般是用来求给定n组顺序关系(排名,辈分,大小、、、),判断给出的信息是不是留存争辩,求出符合条件的一一,如需供给出最小的则必要动用大根堆。拓扑排序还足以用来找3个图中的最长链,也足以用来找从三个点出发的最长链(经过3个点的最长链)不过要先预处理一下

10个世居民族的灿烂风情和多姿多彩多彩的文化;丝路上长时间的历史古迹。高山,冰川,雅丹,
密林,湖泊,大漠,高原,戈壁,胡杨草甸,都是山东最怪异的当然风景,还有多达30种档次诸多自然爱戴区。无数自然的,人文的,历史的景致,一路上能够有繁不胜数的咬合。
辽宁的具有最丰盛最新鲜的色彩和转换。

          六、二分图

微小顶点覆盖是指最少的顶点数使得二分图中的每条边都至少与中间多少个点相关联,二分图的微小顶点覆盖数=二分图的最大匹配数;

小小的路径/点覆盖是指用尽量少的不相交路径覆盖二分图中的全部终端。二分图的微乎其微路径覆盖数=|V|-二分图的最大匹配日数    

最大独立集(指寻找二个点集,使得个中任意两点在图中无对应边)=|V|-二分图的最大匹配数

澳门

习题:

安特卫普看作2个腐败样样俱全的城市,不仅能让你感触到自然风景的特种魅力,还会让您的舌尖满足全数追求。可爱的大花熊,绝美的九寨沟,令人垂涎三尺的火锅,一切令人忍俊不禁……

洛谷——P2756 飞银行职员配对方案难点

KM算法

​苏州

洛谷——P3386 【模板】二分图匹配

图片 3图片 4

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010
using namespace std;
bool vis[N];
int n,m,e,x,y,ans,girl[N],map[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int find(int x)
{
    for(int i=1;i<=m;i++)
     if(map[x][i]&&!vis[i])
     {
         vis[i]=true;
         if(find(girl[i])||!girl[i])
         {
             girl[i]=x;
             return true;
         }
     }
     return false;
}
int main()
{
    n=read(),m=read(),e=read();
    for(int i=1;i<=e;i++)
     x=read(),y=read(),map[x][y]=1;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(find(i)) ans++;
    }
    printf("%d",ans);
    return 0;
}

二分图最大匹配

​四川腾冲、和顺古村

1.最短路模板题

内景的拍片地基本都在和顺古城。和顺镇位居腾冲县城西南4英里处古名“阳温墩”,由于小河绕村而过,故改名“河顺”,后取
“士和民顺”之意,雅化为今名,现称和顺镇是广西老牌的侨乡。和顺古村落有成都百货上千盛名的山山水水:和顺长春宫、古树群、洗衣亭、Sammo Hung阁等等。

洛谷——P3379 【模板】近来集体祖先(LCA)

图片 5图片 6

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 501000
using namespace std;
int n,m,tot,x,y,root;
int fa[N],top[N],size[N],deep[N],head[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N<<1];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int dfs(int x)
{
    size[x]=1;
    deep[x]=deep[fa[x]]+1;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(fa[x]==t) continue;
        fa[t]=x,dfs(t);
        size[x]+=size[t];
    }
}
int dfs1(int x)
{
    int t=0;
    if(!top[x]) top[x]=x;
    for(int i=head[x];i;i=edge[i].next)
    {
        int to=edge[i].to;
        if(fa[x]!=to&&size[t]<size[to]) t=to;
    }
    if(t) top[t]=top[x],dfs1(t);
    for(int i=head[x];i;i=edge[i].next)
    {
        int to=edge[i].to;
        if(fa[x]!=to&&to!=t) dfs1(to);
    }
}
int LCA(int x,int y)
{
    for(;top[x]!=top[y];x=fa[top[x]])
     if(deep[top[x]]<deep[top[y]])
      swap(x,y);
    if(deep[x]>deep[y]) swap(x,y);
    return x;
}
int main()
{
    n=read(),m=read(),root=read();
    for(int i=1;i<n;i++)
     x=read(),y=read(),add(x,y),add(y,x);
    dfs(root),dfs1(root);
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read();
        printf("%d\n",LCA(x,y));
    }
    return 0;
}

LCA模板

 

​不知不觉大家来了早已播放了比比皆是期,相信我们一定有很多的记住和不舍,那么我们能够顺着这么些途径,回味下女神所留下的足迹。

洛谷——P2661 消息传递 && cogs619. [钱塘中学二〇〇七] 传话

传送门 

图片 7消息传送tarjan判环

图片 8传话:图片 9

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 10010
using namespace std;
bool vis[N];
int n,m,x,y,tot,tim,sum,top;
int s[N],dfn[N],low[N],head[N],stack[N],belong[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tarjan(int x)
{
    dfn[x]=low[x]=++tim;
    stack[++top]=x;vis[x]=true;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(vis[t]) low[x]=min(low[x],dfn[t]);
        else if(!dfn[t]) tarjan(t),low[x]=min(low[x],low[t]);
    }
    if(low[x]==dfn[x])
    {
        sum++;belong[x]=sum,s[sum]++;
        for(;stack[top]!=x;top--)
        {
            s[sum]++;
            vis[stack[top]]=false;
            belong[stack[top]]=sum;
        }
        vis[x]=false;top--;
    }
}
int main()
{
    freopen("messagez.in","r",stdin);
    freopen("messagez.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read();
        add(x,y);
    }
    for(int i=1;i<=n;i++)
     if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
     if(s[belong[i]]==1) printf("F\n");
     else printf("T\n");
    return 0;
}

tarjan判环

tarjan判环,能够用来缓解以下难点   1.一人发生的信息是或不是能传给自个儿 2.一群人玩游戏,假设自个儿的新闻被人家告诉那么游戏甘休,问最少举行的轮数,用tarjan求最小环中的点的个数

西安园林人人皆知,园林都有它特殊的魔力,林中的一山一水、一花一草,都独具生活里安闲、舒适的象征,只要您逐级的分享这一体,荷塘里的水芸会为你笑上一笑,树上的鸟鸣都会为您唱上一曲。

题意:给你贰个N*N的矩阵,有局地格子里有小行星,未来你有一部分威力不小的炮弹,每三次发射都能够消灭掉矩阵中一行或一列的小行星,求最少射击次数

题解:行列匹配,对于那种每一回只可以消灭一行或许一列的操作,我们能够将每一行看成一个会见,将每一列看成二个会合,然后跑二分图匹配,标题中供给最少多少步可以将小点儿全部扑灭,就是让求最少用多少条边将有着的点全体覆盖,也正是最小点覆盖难点=最大匹配数,然后正是个模板题了

 

图片 10图片 11

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010
using namespace std;
bool vis[N];
int n,m,e,x,y,ans,girl[N],map[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int find(int x)
{
    for(int i=1;i<=n;i++)
     if(map[x][i]&&!vis[i])
     {
         vis[i]=true;
         if(find(girl[i])||!girl[i])
         {
             girl[i]=x;
             return 1;
         }
     }
    return 0;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
     x=read(),y=read(),map[x][y]=1;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(find(i)) ans++;
    }
    printf("%d",ans);
    return 0;
 } 

队列匹配:每趟只好消灭一行或然一列,问最少多少步可以将。。全体扑灭

 

​金斯敦威墨西塔什干人简介里士满威奥马哈人是1个集酒吧、饮食、购物、住宿、会议及展览、欢乐、娱乐等于一体的重型综合度假村。坐上小船,听威利亚商家高歌一曲,很好听的啊!

洛谷——P2176 [USACO14FEB]路障Roadblock

题意:在 M
条路的某一条上安放一叠稻草堆,使那条路的长短加倍,接纳一条路困扰使得FJ
从家到牛棚的路长扩大最多,求最大增量

题解:大家理解更改最短路上的边才会使路径长度爆发变化,因而大家先跑2回最短路,然后记录最短路上的每一条路线,然后暴力枚举每一条路子将其路径长度增为两倍,然后在跑最短路,跑出的最短路与第3次的最短路相减即为答案

小总计:一 、对于那种变动一条边,使s到t的门路增量最大,先跑最短路,记录路径,唯有改变路线上的点才会使s到t的最短路长度爆发变更,由此暴力枚举最短路上的每一条边,看那条边改变后导致的影响最大

贰 、改变图中一条边的新闻,然后求最短路等等的题材,一般是建反向边,跑五遍spfa,然后最短路长度为从1点到那条路线s节点的不二法门+那条边的长短+终点到那条边的t节点的长度

图片 12图片 13

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 5100
using namespace std;
queue<int>q;
bool vis[N];
int n,m,x,y,z,tot=1,ans1,ans2,sum;
int fa[N],dis[N],head[N],node[N],num[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f; 
}
struct Edge
{
    int to,dis,next,from;
}edge[N<<1];
int add(int x,int y,int z)
{
    tot++;
    edge[tot].to=y;
    edge[tot].dis=z;
    edge[tot].next=head[x];
    head[x]=tot;
}
int spfa(int s)
{
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f3f3f3f,sizeof(dis));
    vis[s]=true,q.push(s),dis[s]=0;
    while(!q.empty())
    {
        int x=q.front();q.pop();vis[x]=false;
        for(int i=head[x];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if(dis[to]>dis[x]+edge[i].dis)
            {
                dis[to]=dis[x]+edge[i].dis;
                fa[to]=x;num[to]=i;
                if(vis[to]) continue;
                q.push(to),vis[to]=true;
             }
        }
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        add(x,y,z),add(y,x,z);
    }
    memset(fa,-1,sizeof(fa));
    spfa(1),ans1=dis[n];
    for(int i=n;i!=-1;i=fa[i])
      node[++sum]=num[i];
    for(int i=1;i<=sum;i++)
    {
        edge[node[i]].dis*=2;
        edge[node[i]^1].dis*=2;
        spfa(1);
        ans2=max(ans2,dis[n]);
        edge[node[i]].dis/=2;
        edge[node[i]^1].dis/=2;
    }
    printf("%d",ans2-ans1);
    return  0;
}

求使哪条边的长短改变使s到t的门路长度增量最大

小初精心为你整理出这几个风景,不要枉费笔者的意在哦!趁着中秋节和国庆,一起出来耍吧!

洛谷——P2868 [USACO07DEC]骑行奶牛Sightseeing Cows

题解:奶牛们从源点出发然后在回去源点,也正是说奶牛走过的路子为多个环,在奶牛走的这么些环中ans=全数的乐趣数/路上海消防耗的享有的年月。

笔者们将上面包车型地铁架子进行变形,可以博得路上海消防耗的有所时间*ans=全体的乐趣数。——>路上海消防耗的兼具时间*ans-全体的意趣数=0;

接下来大家在开始展览二分答案,处理出ans,然后对于每二个ans跑n个spfa,判断是不是留存负环,如若存在负环就认证当前方案不是最好答案(存在负环条件:当前点入队次数抢先n,当然那种状态是当我们用bfs的spfa时),让大家用dfs的spfa时,大家用3个bool变量表示那一个数有没有被访问过,如若被访问过,表达她出现了负环直接甘休循环。我们的ans仍可以有更大的解,即当路上海消防耗的享有的时间*ans-全体的乐趣数<0时大家的ans不是眼前最优解继续创新最优解

只顾:二分结束的标准化为r-l>0.000001,不然会爆long long

图片 14图片 15

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 51000
using namespace std;
bool vis[N];
int n,m,x,y,z,tot;
int c[N],num[N],head[N];
double ans,mid,l,r,w[N],dis[N];
struct Edge
{
    int to,dis,from,next;
}edge[N];
int add(int x,int y,int z)
{
    tot++;
    edge[tot].to=y;
    edge[tot].dis=z;
    edge[tot].next=head[x];
    head[x]=tot;
}
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int spfa(int x)
{
    vis[x]=true;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(dis[t]>dis[x]+w[i])
        {
            dis[t]=dis[x]+w[i];
            if(vis[t]||spfa(t))
            {
                vis[x]=false;
                return true;
            }  
        }
    }
    vis[x]=false;
    return false;
}
int pd()
{
    for(int i=1;i<=n;i++)
        if(spfa(i)) return true;
    return false;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) c[i]=read();
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        add(x,y,z);
    }
    l=0,r=100005;
    while(r-l>0.0000001)
    {
        mid=(l+r)/2;
        for(int i=1;i<=tot;i++)
        {
            int t=edge[i].to;
            w[i]=(double)mid*edge[i].dis-c[t];
        }
        if(pd())
        {
            ans=mid;
            l=mid;
        }
        else r=mid;
    }
    printf("%.2lf",ans);
    return 0;
}

二分+dfs判负环

 

腾冲是闻名的侨乡、文献之邦和翡翠集散地,也是省级历史文化名城。腾冲在明清时称滇越,六安国先前时代设腾冲府。由于地理地方首要,历代都派重兵驻守,孙吴还修筑了石头城,称之为“极边第2城”。

洛谷—— P3385 【模板】负环

spfa判负环dfs版(dfs要比bfs的spfa判负环跑的快)

图片 16图片 17

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 200010
using namespace std;
bool vis[N],vist;
int n,m,t,x,y,z,tot,head[N],dis[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,dis,next;
}edge[N<<1];
int add(int x,int y,int z)
{
    tot++;
    edge[tot].to=y;
    edge[tot].dis=z;
    edge[tot].next=head[x];
    head[x]=tot;
}
int spfa(int x)
{
    vis[x]=true;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(dis[t]>dis[x]+edge[i].dis)
        {
            dis[t]=dis[x]+edge[i].dis;
            if(vis[t]||vist)
            {
                vist=true;
                break;
            }
            spfa(t);
        }
    }
    vis[x]=false;
}
void begin()
{
    tot=vist=0;
    memset(dis,0,sizeof(dis));
    memset(head,0,sizeof(head));
    memset(vis,false,sizeof(vis));
}
int main()
{
    t=read();
    while(t--)
    {
        begin();
        n=read(),m=read();
        for(int i=1;i<=m;i++)
        {
            x=read(),y=read(),z=read();
            add(x,y,z);
            if(z>=0) add(y,x,z);
        } 
        for(int i=1;i<=n;i++)
        {
            spfa(i);
            if(vist) break;
        }
        if(vist) printf("YE5\n");
        else printf("N0\n");
    }
    return 0;
}

dfs的spfa判负环

还记得二〇一八年暑期档的《偶像来了》吗?这些节目开启了“女综时期”。经历了名字、队伍容貌大换血之后,第②季《大家来了》,“歌星女神生存体验秀”又再一次启程。赵雅芝(zhào yǎ zhī )、刘嘉玲女士、莫文蔚(mò wén wèi )、陈Jon女士、江一燕(Jiang Yiyan)、谢娜(xiè nà )、徐娇(xú jiāo )、曲迪娜,那多个平常八竿子打不到联合的女明星,组成了一幅和谐的画面。那么每一期的景点拍片地也成了大家争向前往的地点,具体是哪呢,跟着小初中一年级起走啊!

         六、LCA

​宽窄巷子作为伊斯兰堡的一张片子,已经化为外省游客到萨格勒布必去的风光之一,在此间,你可以尝尝到最精美的好吃的食品佳肴,体会到最民风民俗的川北风情。

 HDU——1150 Machine Schedule

题意:给出两台机器A、B,机器A上有n种形式,机器B上有m种形式,现有k个必要周转的义务,每种职责有对应的运转情势,(i, x, y)表示i职务对应的A B机器上的运作格局为x,y. 开首的干活方式都以0.每台机械上的职务能够依照自由顺序执行,可是每台机械每转换三次格局要求重启三回。求机重视启的足足次数

题解:大家对于每二个任务连边,然后我们今天要到位任务并且大家要讲求机注重启的次数最少,那么也等于说咱俩必要用到最少的点把具有的边连起来,这样就转化成了最小点覆盖的裸题了                
最小点覆盖等于最大匹配数

图片 18图片 19

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010
using namespace std;
bool vis[N];
int n,m,k,x,y,z,ans,girl[N],map[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int find(int x)
{
    for(int i=1;i<=m;i++)
    {
        if(!vis[i]&&map[x][i])
        {
            vis[i]=true;
            if(girl[i]==-1||find(girl[i])) {girl[i]=x; return 1;}
        }
    }
    return 0;
}
int main()
{
    while(1)
    {
        n=read();if(n==0) break;
        m=read(),k=read();ans=0;
        memset(map,0,sizeof(map));
        memset(girl,-1,sizeof(girl));
        for(int i=1;i<=k;i++)
        {
            z=read(),x=read(),y=read();
            map[x][y]=1;
        }
        for(int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            if(find(i)) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

最小点覆盖

斯特Russ堡,邂逅极具西Ante色的价值观文化。感受着江南卓越的小乔流水人家,再喝上一瓶小酒,听着马尔默弹唱,假若再去埃德蒙顿园林走上一走,江南的气韵大抵你都尝尝到了啊!

洛谷——P1629 邮递员送信

题解:从1点到各样点的最短路很好求,那么从各种点到1点的最短路的长短呢?我们建反向边,那么从前能够反向到达1点的点大家都得以从1点到达,那么大家在跑一边spfa,求出从1点到任何点的最短路即为从其余点到1点的最短路的尺寸。

图片 20图片 21

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 300010
#define maxn 999999
using namespace std;
queue<int>q;
bool vis[N];
long long ans;
int n,m,x,y,z,tot,tot1;
int dis[N],dis1[N],head[N],head1[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,dis,next;
}edge[N],edge1[N];
int add(int x,int y,int z)
{
    tot++;
    edge[tot].to=y;
    edge[tot].dis=z;
    edge[tot].next=head[x];
    head[x]=tot;
}
int add1(int x,int y,int z)
{
    tot1++;
    edge1[tot1].to=y;
    edge1[tot1].dis=z;
    edge1[tot1].next=head1[x];
    head1[x]=tot1;
}
int spfa(int s)
{
    for(int i=1;i<=n;i++) dis[i]=maxn,vis[i]=false;
    q.push(s),vis[s]=true,dis[s]=0;
    while(!q.empty())
    {
        x=q.front();q.pop(),vis[x]=false;
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            if(dis[t]>dis[x]+edge[i].dis)
            {
                dis[t]=dis[x]+edge[i].dis;
                if(vis[t]) continue;
                q.push(t),vis[t]=false;
            }
        }
    }
}
int spfa1(int s)
{
    for(int i=1;i<=n;i++) dis1[i]=maxn,vis[i]=false;
    q.push(s),vis[s]=true,dis1[s]=0;
    while(!q.empty())
    {
        x=q.front();q.pop(),vis[x]=false;
        for(int i=head1[x];i;i=edge1[i].next)
        {
            int t=edge1[i].to;
            if(dis1[t]>dis1[x]+edge1[i].dis)
            {
                dis1[t]=dis1[x]+edge1[i].dis;
                if(vis[t]) continue;
                q.push(t),vis[t]=false;
            }
        }
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        add(x,y,z),add1(y,x,z);
    }
    spfa(1),spfa1(1);
    for(int i=2;i<=n;i++)
     ans+=(long long)dis[i]+dis1[i];
    printf("%lld",ans);
    return 0;
}

求从1点到持有点然后在从全部点重返1点的最短路的长短,注意我们2回只好到达三个点

Madison有着赌城、赌埠之称,与蒙特卡洛、坎Pina斯并称之为世界三大赌城,目前主要有葡京、新葡京、永利、星际、美高梅、皇冠、金沙、威孟菲斯人等赌场,假设您想试试手气,能够小玩一把哦!

HDU——1285 明确竞技排名

给出n个排名关系,求出符合条件的排行顺序,输出字典序最小的答案

拓扑排序模板题,使用优先队列来使答案的字典序最小

图片 22图片 23

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 510
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;
int n,m,x,y,tot,sum,in[N],ans[N],head[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,dis,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tpsort()
{
    for(int i=1;i<=n;i++)
     if(in[i]==0) q.push(i);
    while(!q.empty())
    {
        x=q.top(),q.pop(),sum++,ans[sum]=x;
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            in[t]--;
            if(in[t]==0) q.push(t);
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        sum=0,tot=0;
        memset(in,0,sizeof(in));
        memset(ans,0,sizeof(ans));
        memset(head,0,sizeof(head));
        memset(edge,0,sizeof(edge));
        for(int i=1;i<=m;i++)
         x=read(),y=read(),add(x,y),in[y]++;
        tpsort();
        for(int i=1;i<sum;i++)
         printf("%d ",ans[i]);
        printf("%d\n",ans[sum]);
    }
    return 0;
}

加以排行关系,求排列顺序

洛谷——P2196 挖地雷

题意:求从随机一个点先导可以挖到的地雷的总和最多,求地雷总数及挖的路径

题解:看数量范围n<=20所以大家能够跑n边spfa,枚举从每三个点起来所能挖到的最多的地雷的数量,即便当前路线的末尾挖到的地雷数最多,则证实那条道路最优,假若我们美国剧到的门径更优,大家立异答案,更新路径

图片 24图片 25

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 300
using namespace std;
queue<int>q;
bool vis[N];
int n,m,x,y,e,tot,ans,maxn,sum;
int a[N],fa[N],pre[N],dis[N],head[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,dis,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int spfa(int s)
{
    for(int i=1;i<=n;i++) dis[i]=0,vis[i]=false;
    q.push(s),vis[s]=true,dis[s]=a[s];
    while(!q.empty())
    {
        x=q.front();q.pop(),vis[x]=false;
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            if(dis[t]<=dis[x]+a[t])
            {
                dis[t]=dis[x]+a[t];
                fa[t]=x;
                if(vis[t]) continue;
                q.push(t),vis[t]=true;
            }
        }
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(x=1;x<n;x++)
    {
        for(int y=x+1;y<=n;y++)
        {
            e=read();
            if(e) add(x,y);
        }
    }
    for(int s=1;s<=n;s++)
    {
        maxn=ans;
        memset(fa,-1,sizeof(fa));
        spfa(s);
        for(int i=1;i<=n;i++) 
         if(ans<dis[i]) e=i,ans=dis[i];
        if(ans==maxn) continue;
        sum=0;
        for(;e!=-1;e=fa[e])
         pre[++sum]=e;
    }
    for(int i=sum;i>=1;i--)
     printf("%d ",pre[i]);
    printf("\n%d",ans);
    return 0;
}

求最长路经及其长度

HDU——3342 Legal or Not

给出n对各类关系,询问所提交的涉及是不是合法,即为不相互顶牛

题解:拓扑排序判环,当拓扑排序入队的点的个数少于n时,即为出现了环

图片 26图片 27

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 510
using namespace std;
queue<int>q;
int n,m,x,y,tot,sum,in[N],head[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
bool tpsort()
{
    for(int i=1;i<=n;i++) 
     if(in[i]==0) q.push(i);
    while(!q.empty())
    {
        x=q.front(),q.pop(),sum++;
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            in[t]--;
            if(in[t]==0) q.push(t);
        }
    }    
    if(sum==n) return true;
    return false;
}
int main()
{
    while(1)
    {
        n=read(),m=read();
        if(n==0&&m==0) break;
        tot=0,sum=0;
        memset(in,0,sizeof(in));
        memset(head,0,sizeof(head));
        memset(edge,0,sizeof(edge));
        for(int i=1;i<=m;i++)
         x=read(),y=read(),add(x+1,y+1),in[y+1]++;
        if(tpsort()) printf("YES\n");
        else printf("NO\n");
    }
    return  0;
}

给出m对一一的关系,询问给出的涉嫌是还是不是抵触

T3 BZOJ——1821: [JSOI2010]Group 部落划分 Group

题解:要将那n各部落全部连接起来要求n-1条边,大家要将它划分成m个部落,相当于说大家要有m个部落无法被连接起来,大家要先将离开小的多少个部落先连起来,那就刚刚是最小生成树的思考,所以要想到最小生成树也不难。大家将那m个部落连起来正好是用m-1条边,也等于说大家将除了这m个群众体育连起来,恰好是要用n-1-(m-1)也正是n-m条边,然后那m部落间的蝇头的偏离便是n-m+1条边的长短了

图片 28图片 29

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010
using namespace std;
double ans;
int n,k,x,y,s,fx,fy,sum,fa[N],xx[N],yy[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int x,y;
    double z;
}edge[N*N];
int cmp(Edge a,Edge b)
{return a.z<b.z;}
int find(int x)
{
    if(x==fa[x]) return  x;
    fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    n=read(),k=read();
    for(int i=1;i<=n;i++) xx[i]=read(),yy[i]=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      if(i!=j)
      {
          ++s;
          edge[s].x=i;
          edge[s].y=j;
          edge[s].z=sqrt((double)pow(xx[i]-xx[j],2)+(double)pow(yy[i]-yy[j],2));
      }
    for(int i=1;i<=n;i++) fa[i]=i;
    sort(edge+1,edge+1+s,cmp);
    for(int i=1;i<=s;i++)
    {
        x=edge[i].x,y=edge[i].y;
        fx=find(x),fy=find(y);
        if(fa[fx]==fy) continue;
        fa[fx]=fy;sum++;
        if(sum==n-k+1) {ans=edge[i].z;break;}
    }
    printf("%.2lf",ans);
    return 0;
}

求将n个群众体育划分成m个,使那么些群众体育间的最短距离最大,求那m部落间最短距离

洛谷—— P3469 [POI2008]BLO-Blockade

传送门

题解:我们知晓割掉3个点后,能够对整张图的不连通有序对导致影响的肯定为割点,因而,大家用tarjan处理,大家将那几个图看成一颗树,将点割掉未来不连贯的有序对为这么些典型树内的点的·个数*其一点阿爸里面包车型的士点的个数。那样我们又能识破他们之间无法循环不断的罗列就是她的老爹节点内的个数*塔子树节点内的个数、。即ans【i】=t*(n-t-1)
t=size[i]
那样大家求出了从该点外不能够互相到达的对数。最后再添加由于这几个点割去了,因而这些点不能够到达全体的点。最终乘2

图片 30图片 31

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 500010
#define ll long long
using namespace std;
ll ans[N];
int n,m,x,y,tot,tim;
int dfn[N],low[N],head[N],size[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N<<1];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tarjan(int x)
{
    int z=0;size[x]=1;
    dfn[x]=low[x]=++tim;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(!dfn[t])
        {
            tarjan(t);size[x]+=size[t];
            low[x]=min(low[x],low[t]);
            if(dfn[x]<=low[t])
            {
                ans[x]+=(ll)z*size[t];
                z+=size[t];
            }
        }
        else low[x]=min(low[x],dfn[t]);
    }
    ans[x]+=(ll)z*(n-z-1);
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read();
        add(x,y),add(y,x);
    }
    tarjan(1);
    for(int i=1;i<=n;i++)
     printf("%lld\n",(ans[i]+n-1)<<1);
    return 0;
}

割掉二个点后,不连通的对数

 

最受欢迎的牛类的类的题材,大家需求tarjan缩点然后初度为零的点即为最受欢迎的牛

              二、割边、割点

习题:

洛谷——P2966 [USACO09DEC]牛收费路径Cow Toll Paths

题意:点有点权,边有边权,从1个点到另二个点的花费,是透过的具有道路的过路费之和,加上经过的具有的城市(包含起源和顶峰)的过路费的最大值,给定k个询问,求从s到t的最大值,如不连通输出-1

题解:Floyd,将点权进行排序,然后跑Floyd,大家在依次跑Floyd的时候保障k=n是的k的点权最大,也即是说那里大家在更新i到j那条路径的时候依次是使的中级节点的点权最大,那样保障中间节点的点权最大,这样就无须找每条途径上的最大点权了

图片 32图片 33

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define N 300
#define maxn 9999999
using namespace std;
int n,m,x,y,z,p,dy[N],dis[N][N],f[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct Node
{
    int c,num;
}node[N];
int cmp(Node a,Node b)
{return a.c<b.c;}
int main()
{
    n=read(),m=read(),p=read();
    for(int i=1;i<=n;i++) node[i].c=read(),node[i].num=i;
    sort(node+1,node+1+n,cmp);
    for(int i=1;i<=n;i++) dy[node[i].num]=i;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      f[i][j]=dis[i][j]=maxn;
    for(int i=1;i<=n;i++) f[i][i]=0;
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        x=dy[x],y=dy[y];
        f[x][y]=f[y][x]=min(f[x][y],z);
    }
    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],f[i][k]+f[k][j]);
        dis[i][j]=min(dis[i][j],f[i][j]+max(max(node[i].c,node[k].c),node[j].c)); 
      }
    while(p--)
    {
        x=read(),y=read();
        x=dy[x],y=dy[y];
        if(dis[x][y]>=maxn) printf("-1\n");
        else printf("%d\n",dis[x][y]);
    }
    return 0;
}

求从2个点到另1个点的最短路的,定义为边权+最大点权

codevs——2645 Spore

为从星系1 到星系N 的微小代价的途径的代价. 假若这么的路径不设有,输出’No
such path’.                     spfa判负环裸题,bfs版    
线路不设有即为出现了负环

图片 34图片 35

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 200010
#define maxn 9999999
using namespace std;
queue<int>q;
bool vis[N],vist;
int n,m,w,x,y,z,tot,head[N],dis[N],in[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,dis,next;
}edge[N<<1];
int add(int x,int y,int z)
{
    tot++;
    edge[tot].to=y;
    edge[tot].dis=z;
    edge[tot].next=head[x];
    head[x]=tot;
}
void begin()
{
    tot=vist=0;
    memset(in,0,sizeof(in));
    memset(dis,0,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(head,0,sizeof(head));
}
int spfa(int s)
{
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++) dis[i]=maxn,vis[i]=false;
    vis[s]=true,dis[s]=0,q.push(s);
    while(!q.empty())
    {
        x=q.front(),q.pop();vis[x]=false;
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            if(dis[t]>dis[x]+edge[i].dis)
            {
                dis[t]=dis[x]+edge[i].dis;
                if(!vis[t]) in[t]++,vis[t]=true,q.push(t);
                if(in[t]>n) return true;
            }
        }
    }
    return false;
}
int main()
{
     while(1)
    {
        begin();
        n=read(),m=read();
        if(n==0&&m==0) break;
        for(int i=1;i<=m;i++)
        {
            x=read(),y=read(),z=read(),w=read();
            add(x,y,z),add(y,x,w);
        }
        vist=spfa(1);
        if(vist||dis[n]==maxn) printf("No such path\n");
        else  printf("%d\n",dis[n]);
    }
    return 0;
}

bfs判负环,不要忘了伊始化!

洛谷——P3225 [HNOI2012]矿场搭建

传送门

题解:大家渴求最少修建多少个逃生处才能使任意3个地点炸了独具的人都能够逃生。我们只要将割点炸掉以往,这几个图中毫无疑问会变成少数个双连通分量,大家得以明白固然多少个双连通分量中从不割点,那么大家必要建多少个逃生处扎了三个仍是能够去另一个;要是老是一个割点,大家只需求在双连通图中国建工业总会公司多个就好了,炸了割点还是能逃生;要是链接多个,那些无论炸那1个大家都能透过另一个逃生

图片 36图片 37

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 510
using namespace std;
long long ans2;
bool vis[N],cut_point[N],cut_edge[N];
int head[N<<1],dfn[N],low[N],belong[N];
int n,m,x,y,s,tim,cut,tot,sum,cnt,ans1;
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N<<1];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tarjan(int x,int pre)
{
    int sum=0;bool boo=false;
    dfn[x]=low[x]=++tim;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if((1^i)==pre) continue;
        if(!dfn[t])
        {
            sum++;tarjan(t,i);
            low[x]=min(low[x],low[t]);
            if(low[t]>dfn[x]) cut_edge[i/2]=true;
            if(low[t]>=dfn[x]) boo=true;    
        }
        else low[x]=min(low[x],dfn[t]);    
    }    
    if(pre==-1){if(sum>1) cut_point[x]=true;}
    else if(boo) cut_point[x]=true;
}
int begin()
{
    n=tim=ans1=0;tot=ans2=1;
    for(int i=1;i<=N;i++)
    {
        vis[i]=cut_point[i]=0;
        dfn[i]=low[i]=belong[i]=0;
     } 
    memset(head,0,sizeof(head));
    memset(edge,0,sizeof(edge));
}
int dfs(int x)
{
    s++;vis[x]=true;belong[x]=sum;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(cut_point[t]&&belong[t]!=sum)  cut++,belong[t]=sum;
        if(!vis[t]&&!cut_point[t]) dfs(t);
    }
}
int main()
{
    while(1)
    {
        m=read();
        if(m==0) break;begin();
        for(int i=1;i<=m;i++)
        {
            x=read(),y=read();
            add(x,y),add(y,x);
            n=max(n,max(x,y));
        }
        for(int i=1;i<=n;i++)
         if(!dfn[i]) tarjan(i,-1);
        sum=0;
        for(int i=1;i<=n;i++)
        {
            if(vis[i]||cut_point[i]) continue;
            s=cut=0,sum++;dfs(i);
            if(!cut) ans1+=2,ans2*=(long long)(s*(s-1))>>1;
            if(cut==1) ans1++,ans2*=(long long)s;
        }
        printf("Case %d: %d %lld\n",++cnt,ans1,ans2);
    }
    return 0;
}

tarjan+dfs

当损坏两个点或一条边就不能够是国际图书馆协会联合会通的时候,就供给割点割边

HDU——2647 Reward

总老董要发工钱,每一种人都有一个渴求,他必须比哪个人的薪给高,基准报酬为888,求共最少必要发多少工钱

题解:拓扑排序分层考虑,每一层一种薪水,怎么判断是那一层?对于基层我们是领会他在哪一层的,然后大家每3个入队的点的层数一定是链接他的不行在队中的点的层数+1

图片 38图片 39

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 20100
#define money 888
using namespace std;
queue<int>q;
long long anss;
int n,m,x,y,s,in[N],tot,sum,head[N],ans[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct Edge
{
    int from,to,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
void begin()
{
    s=0,sum=0,tot=0;anss=0;
    memset(in,0,sizeof(in));
    memset(ans,0,sizeof(ans));
    memset(head,0,sizeof(head));
    memset(edge,0,sizeof(edge));
}
int tpsort()
{
    for(int i=1;i<=n;i++)
     if(in[i]==0) q.push(i),ans[i]=0;
    while(!q.empty())
    {
        x=q.front();q.pop(),sum++;
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            in[t]--;
            if(in[t]==0) ans[t]=ans[x]+1,q.push(t);
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        begin();
        for(int i=1;i<=m;i++)
        {
            x=read(),y=read();
            add(y,x),in[x]++;
        }
        tpsort();
        if(sum!=n) printf("-1\n");
        else 
        {
            for(int i=1;i<=n;i++)
             anss+=money+ans[i];
            printf("%lld\n",anss);
        }       
    }
    return  0;
}

每一层上的人一种报酬,求最少薪俸总额

 

洛谷——P2746 [USACO5.3]校园网Network of Schools

传送门

图片 40图片 41

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010 
using namespace std;
bool vis[N];
int n,m,s1,s2,tot,tim,sum,top,ans1,ans2;
int in[N],out[N],low[N],dfn[N],head[N],stack[N],belong[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tarjan(int x)
{
    dfn[x]=low[x]=++tim;
    stack[++top]=x,vis[x]=true;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(vis[t]) low[x]=min(low[x],dfn[t]);
        else if(!dfn[t]) tarjan(t),low[x]=min(low[x],low[t]);
    }
    if(low[x]==dfn[x])
    {
        sum++;belong[x]=sum;
        for(;stack[top]!=x;top--)
        {
            vis[stack[top]]=false;
            belong[stack[top]]=sum;
        }
        top--,vis[x]=false;
    }
}
int shink_point()
{
    for(int i=1;i<=n;i++)
     for(int j=head[i];j;j=edge[j].next)
     {
         int t=edge[j].to;
         if(belong[i]!=belong[t])
          in[belong[t]]++,out[belong[i]]++;
     }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        while(1)
        {
            m=read();
            if(m==0) break;
            add(i,m);
        }
    }
    for(int i=1;i<=n;i++)
     if(!dfn[i]) tarjan(i);
    shink_point();
    for(int i=1;i<=sum;i++)
    {
        if(in[i]==0) s1++;
        if(out[i]==0) s2++;
     } 
    if(sum==1) ans2=0;
    else ans2=max(s1,s2);
    ans1=s1;
    printf("%d\n%d",ans1,ans2);
    return 0;
}

tarjan求强连通分量

小总计:1.最少让几人领悟就足以做到让全数的人都明白音信,最少知道的人的数目即为缩完点后入读为零的点的个数

    2.最少投入几条边就足以使那个图变成3个强连通图,加的边的条数即为缩完点后Max(入读为零的点的个数,出度为零的点的个数)

poj——3177 Redundant Paths

传送门

求八个无向图在加盟多少条边后改成边双连通图。跟上边的花色大约相同,只可是是三个为有向图,1个为无向图。既然类型相同,那么大家使用相同的想想来做那道题,首先大家先tarjan缩点,然后总括入读为0点的点的个数。有同学肯定要问了,无向图啊,怎么只怕入读为0啊,而且你怎么掌握是入读还是初读的呀,并且tarjan缩点的前提不是有向图吗?那是无向图啊,怎么缩点?
      那即将归功于:if(i==(1^pre))
continue; 对,他是用两条边,不过大家这几个地点只让他走一条边,那样不就和有向图缩点一样了吧?!不知情是入读依然出度,那么间接总结度数为2的点的个数。

图片 42图片 43

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 5005
using namespace std;
bool vis[N];
long long  n,m,x,y,ans,tot=1,tim,sum,top;
long long du[N],dfn[N],low[N],stack[N],belong[N];
long long head[20010];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct Edge
{
    int from,next,to;
}edge[20010];
void add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tarjan(int now,int pre)
{
    dfn[now]=low[now]=++tim;
    stack[++top]=now; 
    for(int i=head[now];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(i==(1^pre)) continue;
        if(!dfn[t]) tarjan(t,i),low[now]=min(low[now],low[t]);
        else low[now]=min(low[now],dfn[t]);
    }
    if(low[now]==dfn[now])
    {
        sum++; belong[now]=sum;
        for(;stack[top]!=now;top--)
          belong[stack[top]]=sum;
        top--;
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
     x=read(),y=read(),add(x,y),add(y,x);
    tarjan(1,0);
    for(int i=1;i<=n;i++)
     for(int j=head[i];j;j=edge[j].next)
      if(belong[i]!=belong[edge[j].to]) du[belong[i]]++,du[belong[edge[j].to]]++;
    for(int i=1;i<=n;i++)
     if(du[i]==2) ans++;
    printf("%d",(ans+1)>>1);
    return 0;
}

无向图缩点

洛谷—— P1347 排序

题意:给您一密密麻麻形如A<B的关系,并供给你认清是还是不是能够基于这几个涉嫌明确那一个数列的逐一

题解:拓扑排序+模拟

图片 44图片 45

#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 110
using namespace std;
int a,b,n,m,s,sum,tot,head[N],in[N],inn[N],p[N];
bool v,unpd,vis[N];
queue<int>q;
char ch;
int read()//在这里我用的读入优化 
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
struct Edge
{
    int from,to,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}//和以前的拓扑排序过程一样,只是多加了几种情况 
int tp()
{
    unpd=false;    v=false; sum=0;//初始数值 
    for(int i=1;i<=26;i++)//开始找入读为一的点 
     {
         inn[i]=in[i];//由于我们要没输入一组后就对该序列进行判断,所以我们新设一个数组inn储存in中的各点入度的值,防止我们下一次在用时,该店的入度值已不是初始值 
         if(!inn[i]&&vis[i])//该点的入读为0并且我们输入过该值 
          {
              if(!v) v=true;//我们要判断有几个入读为0的点,由于如果有两个入读为0的点我们则无法判断他们的关系因为入读为0的点一定是小的,但这两个值得大小我们又无法判断 
              else unpd=true;//unpd用来判断无法判段的情况 
              q.push(i);
              p[++sum]=i;
           } 
      } 
    if(q.empty()) return 1;//如果q数组为空,就说明出现了环,则是存在矛盾的情况。 
    while(!q.empty())//单纯的拓扑排序,但我们要在里面多加一点东西:和前面判断入读为0的方法一样,如果删除一个点以后出现了两个入度为0的边,这样我们将无法判断这两个点的大小 
    {
        int x=q.front();v=false;q.pop();
        for(int i=head[x];i;i=edge[i].next)
        {
            inn[edge[i].to]--;
            if(!inn[edge[i].to])
            {
                q.push(edge[i].to);
                if(!v) v=true;
                else unpd=true;
                p[++sum]=edge[i].to;
            } 
        }
    }
    if(sum!=s) return 1;//说明出现了环。 
    if(unpd) return 2;
    return 0; 
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        cin>>ch,a=ch-64;if(!vis[a]) vis[a]=true,s++;//s是用来存我们输入的元素的个数,方便后面判断s值与sum值的关系(来判断是否为环) 
        cin>>ch;//这个在输入时其实我们也可以用一个数组来表示,在这里我们由于有一个<是没有用的,所以我们直接输入就好了 
        cin>>ch,b=ch-64;if(!vis[b]) vis[b]=true,s++;//vis用来表示该数有值 
        add(a,b);//在这里我们将我们输入的字符转化成了数字,方便后面进行操作 
        in[b]++;//储存入读 
        if(tp()==1) //在这里我们必须让他等于1,因为我们在tp函数中返回的是0,1,2 
        {
            printf("Inconsistency found after %d relations.",i);//存在矛盾 
            return 0;
        }
        if(sum==n&&!tp())//sum=n,说明该序列中所有的数都进行了排序,都能确定他们的位置 
        {
            printf("Sorted sequence determined after %d relations: ",i);
            for(int j=1;j<=n;j++) printf("%c",p[j]+64);//在最开始的时候我竟然让他输出p[i]+64.这告诉我们要注意我们循环使用的变量i,j 
            printf(".");
            return 0;
        }
    }
    printf("Sorted sequence cannot be determined.");//由于我们在tp函数中只有三种情况,除了前两种剩下的就是这一种了。 
    return 0;
}

给定一些涉嫌问是还是不是能经过付出的关系判断全部因素的关系,如若不可能问在第几步后涉及就出现了争论

       一 、强连通分量、缩点

cogs——2419. [HZOI 2016]公路修建2

 题解:现选k种A道路,然后在跑最小生成树,求最小生成树最长边

图片 46图片 47

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 210000
using namespace std;
int n,m,x,y,k,z1,z2,fx,fy,sum,ans,fa[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int x,y,z1,z2;
}edge[N];
int cmp1(Edge a,Edge b)
{return a.z1<b.z1;}
int cmp2(Edge a,Edge b)
{return a.z2<b.z2;}
int find(int x)
{
    if(x==fa[x]) return  x;
    fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    freopen("hzoi_road2.in","r",stdin);
    freopen("hzoi_road2.out","w",stdout);
    n=read(),k=read(),m=read();
    for(int i=1;i<m;i++)
    {
        x=read(),y=read(),z1=read(),z2=read();
        edge[i].x=x;
        edge[i].y=y;
        edge[i].z1=z1;
        edge[i].z2=z2;
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    sort(edge+1,edge+m+1,cmp1);
    for(int i=1;i<=m;i++)
    {
        if(k==0) break;
        x=edge[i].x,y=edge[i].y;
        fx=find(x),fy=find(y);
        fa[fx]=fy;sum++;
        ans=max(ans,edge[i].z1);
        if(sum==k)break;
    }
    sort(edge+1,edge+1+m,cmp2);
    for(int i=1;i<=m;i++)
    {
        x=edge[i].x,y=edge[i].y;
        fx=find(x),fy=find(y);
        if(fx==fy) continue;
        fa[fx]=fy;
        ans=max(ans,edge[i].z2);
    }
    printf("%d",ans);
    return 0;
}

在n个村庄间有三种道路,求至少用k种A道路的使拥有道路连接的最短距离中的最长路径

HDU——2768 Cat vs. Dog

题意:喜欢猫的自然不喜欢狗,喜欢狗的必定不喜欢猫,大家要选则养猫照旧养狗,使满意的人最多

题解: 大家将喜欢猫的和喜欢狗的撤销合并成多个汇聚,然后将那两个汇集中存在争执的点连边,构图,跑二分图匹配,答案及为最大独立集,最大独立集=总点数-最大匹配数

小总结:给出n对喜欢关系,喜欢A的不喜欢B,喜欢B的不喜欢A,大家挑选一位,问最多能是某个个人满意,那一个时候大家将喜欢A的当作三个几何,喜欢B的作为贰个凑合,然后将这七个集聚中存在争辩的连边,跑最大独立集

         四、最短路

洛谷——P1608 路径总结

题解:总结最短路径条数的时候,假诺当前点t是由x更新的,那么到t的最短路条数=到达x点的最短路条数;就算dis[t]=dis[x]+edge[i].dis;最短路径+到达x点的最短路的条数

图片 48图片 49

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 3010
#define maxn 999999
using namespace std;
queue<int>q;
bool vis[N];
int n,m,x,y,z,tot,sum[N],dis[N],f[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int spfa(int s)
{
    for(int i=1;i<=n;i++) dis[i]=maxn,vis[i]=false,sum[i]=1;
    dis[s]=0,vis[s]=true,q.push(s);
    while(!q.empty())
    {
        x=q.front(),q.pop();vis[x]=false;
        for(int t=1;t<=n;t++)
          if(f[x][t])
            if(dis[t]>dis[x]+f[x][t])
            {
                dis[t]=dis[x]+f[x][t];
                sum[t]=sum[x];
                if(vis[t]) continue;
                q.push(t),vis[t]=true;
            }
            else if(dis[t]==dis[x]+f[x][t]) sum[t]+=sum[x];
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      f[i][j]=maxn;
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        f[x][y]=min(f[x][y],z);
    }
    spfa(1);
    if(dis[n]==maxn) printf("No answer");
    else printf("%d %d",dis[n],sum[n]);
    return 0;
}

计算最短路径的条数

洛谷——P2296 寻找道路

题意:在图中找一条从起源到极限的路子,该路线满意路径上的全体点的出边所指向的点都一贯或直接与极端连通且路径最短。

题解:标题中供给大家找到的路子上的点所连得点都必须求与结点直接或直接相连。由此大家建反向边,然后bfs,搜索到终点不能够到达的点,我们就此得以明白,那些点所连接的点都无法在大家所要找的道路中冒出,然后我们在跑一边是spfa求一下最短路就好了

图片 50图片 51

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 200100
using namespace std;
queue<int>q;
bool vis[N],vist[N];
int n,m,s,e,x,y,tot,dis[N],head[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int spfa(int s)
{
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f3f3f3f,sizeof(dis));
    dis[s]=0,vis[s]=true;q.push(s);
    while(!q.empty())
    {
        int x=q.front(); q.pop(); vis[x]=false;
        for(int i=head[x];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if(dis[to]<=dis[x]+1||vist[to]) continue;
            dis[to]=dis[x]+1;
            if(vis[to]) continue;
            q.push(to);    
            vis[to]=true;
        }
    }
}
int bfs(int s)
{
    vis[s]=true;q.push(s);
    while(!q.empty())
    {
        int x=q.front(); q.pop();
        for(int i=head[x];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if(!vis[to]) q.push(to),vis[to]=true;
        }
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
     x=read(),y=read(),add(y,x);
    s=read(),e=read();
    bfs(e);
    for(int i=1;i<=n;i++)
     if(vis[i]==false)
      for(int j=head[i];j;j=edge[j].next)
        vist[edge[j].to]=true;
    for(int i=1;i<=n;i++)
     if(!vis[i]) vist[i]=true;
    spfa(e);
    if(dis[s]>=0x3f3f3f3f) printf("-1");
    else printf("%d",dis[s]);
    return 0;
}

找出一条途径是途径上的点所连接的点都与终极直接或直接相连,并且使那条路线最短

洛谷——P2330 [SCOI2005]繁忙的城市

题解:最少的征途条数即为最小生成树要求加的边的条数即为n-1。第贰问为求最小生成树中的最长边

图片 52图片 53

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 50010
using namespace std;
int n,m,x,y,z,fx,fy,sum,fa[N],ans1,ans2;
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int x,y,z;
}edge[N<<1];
int cmp(Edge a,Edge b)
{return a.z<b.z;}
int find(int x)
{
    if(x==fa[x]) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        edge[i].x=read();
        edge[i].y=read();
        edge[i].z=read();
    }
    sort(edge+1,edge+1+m,cmp);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        x=edge[i].x,y=edge[i].y;
        fx=find(x),fy=find(y);
        if(fx==fy) continue;
        fa[fx]=fy;sum++;
        ans2=max(ans2,edge[i].z);
        if(sum==n-1) break;
    }ans1=n-1;
    printf("%d %d",ans1,ans2);
    return 0;
}

将n个点连通,最少的征程的条数,分值最大的那条道路的分值

 

洛谷——P1119 灾后重建

题意:给出各种村严穆建道路完成的时间,第i个村子重建完毕的时间t[i],能够认为是还要开班重建并在第t[i]天重建完成,并且在当天即可通车。若t[i]为0则印证地震未对此地区造成破坏,一开头就足以通车。之后有Q个询问(x,
y, t),对于各样询问你要回应在第t天,从村庄x到村庄y的最短路径长度为多少

题解:求最短路径,无疑就是最短路的题材了,然后再看数据范围,望着数量范围就应有想到这几个题要用Floyd。然后本题的t的单调递增的质量为大旨下跌了不小的难度,那样我们就不供给看清然后在处理了,在更新最短路的时候大家也不用k
for循环到结尾更新最短路了,因为他说t是干瘪的,大家直接七个while循环,从上一次k不可能创新的职分一向开端,为何?因为在那此前我们能跟新的早已跟新完了,假诺在创新一次就一定于做了无用功

图片 54图片 55

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 210
#define maxn 0x3f3f3f3f
using namespace std;
int n,m,x,y,z,k,T,Q,t[N],dis[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int main()
{
    n=read(),m=read();k=1;
    for(int i=1;i<=n;i++) t[i]=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
       dis[i][j]=maxn;
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        dis[++x][++y]=dis[y][x]=z;
    }
    Q=read();
    while(Q--)
    {
        x=read(),y=read(),T=read();
        x++,y++;
        while(t[k]<=T&&k<=n)
        {
            for(int i=1;i<=n;i++)
             for(int j=1;j<=n;j++)
              dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
            k++;
        }
        if(dis[x][y]>=maxn||t[x]>T||t[y]>T) printf("-1\n");
        else printf("%d\n",dis[x][y]);
    }
    return 0;
}

View Code

洛谷——P3119 [USACO15JAN]草鉴定Grass Cownoisseur

 法一:建反向边,双向dfs找从1点能够达到规定的标准的最长链,能够到达1点的最长链,然后枚举要求反转的边,若能更新最大值,则更新最大值。

图片 56图片 57

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 210000
using namespace std;
queue<int>q;
int n,m,x,y,s,tot,tot1,top,tim;
bool vis[N],vis1[N],vis2[N],vist1[N],vist2[N];
int xx[N],yy[N],in1[N],in2[N],head1[N],head2[N],dfn[N];
int low[N],sum[N],ans1[N],ans2[N],head[N],stack[N],belong[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int to,next;
}edge[N],edge1[N],edge2[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int add1(int x,int y)
{
    tot1++;
    edge1[tot1].to=y;
    edge1[tot1].next=head1[x];
    edge2[tot1].to=x;
    edge2[tot1].next=head2[y];
    head1[x]=head2[y]=tot1;
}
int tarjan(int x)
{
    dfn[x]=low[x]=++tim;
    stack[++top]=x,vis[x]=true;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(vis[t]) low[x]=min(low[x],dfn[t]);
        else if(!dfn[t]) tarjan(t),low[x]=min(low[t],low[x]);
    }
    if(low[x]==dfn[x])
    {
        s++,sum[s]++,belong[x]=s;
        for(;stack[top]!=x;top--)
        {
            sum[s]++;
            vis[stack[top]]=false;
            belong[stack[top]]=s;
        }
        top--,vis[x]=false;
    }
}
int shink_point()
{
    for(int i=1;i<=n;i++)
     for(int j=head[i];j;j=edge[j].next)
     {
         int t=edge[j].to;
         if(belong[i]!=belong[t]) 
          add1(belong[i],belong[t]);
     }
}
int dfs1(int x)
{
    vis1[x]=true;
    for(int i=head1[x];i;i=edge1[i].next)
    {
        int t=edge1[i].to;
        if(ans1[t]<ans1[x]+sum[t])
         ans1[t]=ans1[x]+sum[t],dfs1(t);
    }
}
int dfs2(int x)
{
    vis2[x]=true;
    for(int i=head2[x];i;i=edge2[i].next)
    {
        int t=edge2[i].to;
        if(ans2[t]<ans2[x]+sum[t])
         ans2[t]=ans2[x]+sum[t],dfs2(t);
    }
}
int main()
{
     n=read(),m=read();
    for(int i=1;i<=m;i++)
     xx[i]=read(),yy[i]=read(),add(xx[i],yy[i]);
    for(int i=1;i<=n;i++)
     if(!dfn[i]) tarjan(i);
    shink_point();
    ans1[belong[1]]=ans2[belong[1]]=sum[belong[1]];
    dfs1(belong[1]);dfs2(belong[1]);
    int answer=2*sum[belong[1]];
    for(int i=1;i<=m;i++)
    {
        x=belong[yy[i]],y=belong[xx[i]];
        if(vis1[x]&&vis2[y])
         answer=max(answer,ans1[x]+ans2[y]);
    }
    printf("%d",answer-sum[belong[1]]);
    return 0;
}

求将一条边反转以后图中的最长链的长短

法二:拓扑排序求最长链

们只要直白开始展览拓扑排序的话,大家会发觉到叁个题材:缩完点未来直接总结出来入度为零的点并非是大家所急需的点1,我们要跑最长链的话咱们要求从1点起来跑,也正是说我们的源点必须是1,怎么样形成这点??大家要形成起源是一的话大家务必让1的入度为零,从某个从头更新与她不住的点。从新总括他们的入读,也正是说大家将以此可能出现环的图抽离成一颗树,那棵树的根须为1点。然后再开始展览拓扑排序,找出最长链。

图片 58图片 59

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 210000
using namespace std;
int n,m,x,y,s,tot,tat,top,tim;
bool vis[N],vis1[N],vis2[N];
int xx[N],yy[N],in1[N],in2[N],head1[N],head2[N],dfn[N];
int low[N],sum[N],ans1[N],ans2[N],head[N],stack[N],belong[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct Edge
{
    int to,from,next;
}edge[N],edge1[N],edge2[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int add1(int x,int y)
{
    tat++;
    edge1[tat].to=y;
    edge1[tat].next=head1[x];
    edge2[tat].to=x;
    edge2[tat].next=head2[y];
    head1[x]=head2[y]=tat;
}
int tarjan(int now)
{
    dfn[now]=low[now]=++tim;
    vis[now]=true;stack[++top]=now;
    for(int i=head[now];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(vis[t]) low[now]=min(dfn[t],low[now]);
        else if(!dfn[t]) tarjan(t),low[now]=min(low[t],low[now]);
    }
    if(low[now]==dfn[now])
    {
        s++,belong[now]=s,sum[s]++;
        for(;stack[top]!=now;top--)
          belong[stack[top]]=s,sum[s]++,vis[stack[top]]=false;
        vis[now]=false;top--;    
    }
}
int shink_point()
{
    for(int i=1;i<=m;i++)
     for(int j=head[i];j;j=edge[j].next)
      if(belong[i]!=belong[edge[j].to])
          add1(belong[i],belong[edge[j].to]);  
}
int dfs1(int s)
{
    for(int i=head1[s];i;i=edge1[i].next)
    {
        int t=edge1[i].to;
        if(!in1[t]) dfs1(t);
        in1[t]++;
    }
}
int dfs2(int s)
{
    for(int i=head2[s];i;i=edge2[i].next)
    {
        int t=edge2[i].to;
        if(!in2[t]) dfs2(t);
        in2[t]++;
    }
}
int tpsort(int *in,Edge *edge,int *head,bool *vis,int *ans)
{
    queue<int>q;
    q.push(belong[1]);
    while(!q.empty())
    {
        int x=q.front();q.pop();vis[x]=true;
        for(int i=head[x];i;i=edge[i].next)
        {
            int t=edge[i].to;
            in[t]--;
            if(!in[t]) q.push(t);
            ans[t]=max(ans[t],ans[x]+sum[t]);
        }
    }
}
int main()
{
    n=read(),m=read();
    int answer=0;
    for(int i=1;i<=m;i++)
     xx[i]=read(),yy[i]=read(),add(xx[i],yy[i]);
    for(int i=1;i<=n;i++)
     if(!dfn[i]) tarjan(i);
    shink_point();
    dfs1(belong[1]),dfs2(belong[1]);
    ans1[belong[1]]=ans2[belong[1]]=sum[belong[1]];
    tpsort(in1,edge1,head1,vis1,ans1);
    tpsort(in2,edge2,head2,vis2,ans2);
    answer=2*sum[belong[1]];
    for(int i=1;i<=m;i++)
    {
        x=belong[yy[i]],y=belong[xx[i]];
        if(vis1[x]&&vis2[y])
         answer=max(answer,ans1[x]+ans2[y]);
    }
    printf("%d",answer-sum[belong[1]]);
    return 0;
}

拓扑排序求最长链

2.spfa判负环

求叁个点到另贰个点的最短路径,当以此图中出现负环或两点间没有途径即为不存在从s点到t点的道路,怎么着判定是不是留存负环?跑spfa,倘使1个点入队次数超过n次则证明出现负环

可以组成那篇博客举办复习:http://www.cnblogs.com/z360/p/7363034.html

HDU——3072 Intelligence System

题解:看到那么些题首先想到的就是tarjan缩点然后求最小生成树吧,可是大家会发觉wa掉,为啥,因为我们以此地点是有向图,假设应用最小生成树使他们在三个并查集里,可是其实那一个点恐怕不连贯,所以大家用贪心的考虑,求出于每一个点联通的最短路的长短,然后加上’

图片 60图片 61

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 300010
#define maxn 0x7fffffff
using namespace std;
bool vis[N];
long long ans;
int n,m,x,y,z,s,tot,num,sum,top,tim;
int fa[N],low[N],dfn[N],cost[N],stack[N],head[N],belong[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct Edge
{
    int to,dis,next;
}edge[N];
struct Edde
{
    int to,dis,next;
}edde[N];
int add(int x,int y,int z)
{
    tot++;
    edge[tot].to=y;
    edge[tot].dis=z;
    edge[tot].next=head[x];
    head[x]=tot;
}
void begin()
{
    s=0,tim=0,sum=0,tot=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(head,0,sizeof(head));
    memset(belong,0,sizeof(belong));
}
void tarjan(int now)
{
    dfn[now]=low[now]=++tim;
    stack[++top]=now; vis[now]=true;
    for(int i=head[now];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(vis[t]) low[now]=min(low[now],dfn[t]);
        else if(!dfn[t]) tarjan(t),low[now]=min(low[now],low[t]);
    }
    if(low[now]==dfn[now])
    {
        sum++;belong[now]=sum;
        for(;stack[top]!=now;top--)
        {
            int x=stack[top];
            belong[x]=sum;vis[x]=false;
        }
        vis[now]=false;top--;
    }
}
void work()
{
    ans=0;
    for(int i=1;i<=n;i++)
      for(int j=head[i];j;j=edge[j].next)
      {
          int t=edge[j].to;
          if(belong[i]!=belong[t])
            cost[belong[t]]=min(cost[belong[t]],edge[j].dis);    
     } 
   for(int i=1;i<=sum;i++)
    if(cost[i]!=maxn) ans+=(long long)cost[i];
   printf("%lld\n",ans);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        begin();
        for(int i=1;i<=n;i++) cost[i]=maxn;
        for(int i=1;i<=m;i++)
          x=read(),y=read(),z=read(),add(x+1,y+1,z);
        for(int i=1;i<=n;i++)
         if(!dfn[i]) tarjan(i);
        work();
    }
    return 0;
}

求将n个点连通的最短路的长度,如若存在多少个点在环中,那么那多少个点连通不供给费用

poj —— 1274 The Perfect Stall

题意:给出一些欣赏关系,3头牛只好选则3个货摊,牛唯有在团结喜爱的摊儿上才能产奶,问最多产奶总数

小计算:对于爱好类型(或然一个点能够挑选另一个点)的题材,求最多能满意的人口(对数)一般是二分图求最大匹配裸题

图片 62图片 63

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010
using namespace std;
bool vis[N];
int n,m,k,x,y,ans,girl[N],map[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int find(int x)
{
    for(int i=1;i<=m;i++)
     if(map[x][i]&&!vis[i])
     {
         vis[i]=true;
         if(find(girl[i])||girl[i]==-1)
         {
             girl[i]=x;
             return 1;
         }
     }
     return 0;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        ans=0;
        memset(map,0,sizeof(map));
        memset(girl,-1,sizeof(girl));
        for(int i=1;i<=n;i++)
        {
            k=read();
            while(k--) x=read(),map[i][x]=1;
        }
        for(int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            if(find(i)) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
 } 

最多满足的人数

POJ——SPF

传送门

求删除那些割点后有多少个强连通子图

化解这一类题指标时候大家得以先求出割点,大家有明白low值相同的点在二个强连通分量里,然后我们得以求与该割点相连的点的low值分歧的个数

图片 64图片 65

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 10010
using namespace std;
int n,x,y,tot,tim,cnt,sum;
int dfn[N],low[N],head[N];
bool flag,vis[N],cut_point[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct Edge
{
    int from,next,to;
}edge[N*200];
void add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int begin()
{
    tim=0,tot=1;flag=false;
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cut_point,0,sizeof(cut_point));    
}
int tarjan(int x,int pre)
{
    int s=0; bool boo=false;
    dfn[x]=low[x]=++tim;
    for(int i=head[x];i;i=edge[i].next)
    {
        if((i^1)==pre) continue;
        int t=edge[i].to;
        if(!dfn[t])
        {
            s++,tarjan(t,i);
            low[x]=min(low[t],low[x]);
            if(dfn[x]<=low[t]) boo=true;
        }
        else low[x]=min(low[x],dfn[t]);
    }
    if(pre==-1){if(s>1) cut_point[x]=true;}
    else if(boo) cut_point[x]=true;
}
int main()
{
    while(1)
    {
        x=read();if(x==0) break;
        begin();y=read(),add(x,y),add(y,x);
        n=max(n,max(x,y)),cnt++;
        while(1)
        {
            x=read();
            if(x==0) break;
            y=read(),add(x,y),add(y,x);
            n=max(n,max(x,y));
        }
        printf("Network #%d\n",cnt);
        tarjan(1,-1);
        for(int i=1;i<=n;i++)
         if(cut_point[i])
         {
             sum=0;flag=true;
            memset(vis,0,sizeof(vis));
             for(int j=head[i];j;j=edge[j].next)
             {
                 int t=edge[j].to;
                 if(vis[low[t]]) continue;
                 vis[low[t]]=true,sum++;
            }
            printf("  SPF node %d leaves %d subnets\n",i,sum);
         }
        if(!flag) printf("  No SPF nodes\n");
        printf("\n");
    }
    return 0;
}

剔除割点后联通子图的个数

 

 

              叁 、最小生成树

洛谷——P2047 社交互联网

Floyd预处理从s到t最短路的条数,当四个点时期有边相连的时候我们它的最短路得条数初始化为1,然后大家跑Floyd,当从s到t的最短路须求由k来更新的时候,那么从s到t的最短路的条数即为从s到k最短路的条数*从k到t最短路的·条数,dis[s][k]+dis[k][t]=dis[s][t],那么最短路的条数就要在添加从s到k最短路的条数*从k到t最短路的·条数

图片 66图片 67

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 210
#define maxn 999999
using namespace std;
double ans[N],f[N][N];
int n,m,x,y,z,dis[N][N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      dis[i][j]=(i!=j)*maxn;
    for(int i=1;i<=m;i++)
    {
        x=read(),y=read(),z=read();
        f[x][y]=f[y][x]=1;
        dis[x][y]=dis[y][x]=z;
    }
    for(int k=1;k<=n;k++)
     for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
       if(dis[i][j]>dis[i][k]+dis[k][j])
       {
           f[i][j]=f[i][k]*f[k][j];
           dis[i][j]=dis[i][k]+dis[k][j];
       }
       else 
        if(dis[i][j]==dis[i][k]+dis[k][j])
         f[i][j]+=f[i][k]*f[k][j];
    for(int k=1;k<=n;k++)
     for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
       if(i!=k&&k!=j&&dis[i][j]==dis[i][k]+dis[k][j]&&f[i][j])
        ans[k]+=(double)f[i][k]*f[k][j]/f[i][j];
    for(int i=1;i<=n;i++)
     printf("%.3lf\n",ans[i]);
    return 0;
}

求从s到t的最短路的条数及从求经过一个点的最短路的条数