2018暑假集训楼下第十场(拓扑排序+dijkstra+floyd+Bellman ford)

Sorting It All Out(POJ 1094)
题意:不同值的升序排序序列是用小于号将元素从最小到最大排序的序列。例如,排序序列A,B,C,D意味着A< B,B < C和C < D。在这个问题上,我们会给你一套的关系形成A< B和问你确定是否已指定一个顺序。
输入:包含多组样例。每个实例第一行为两个正整数n和m,n表示要排序的对象数量,其中2 <= n <= 26。要排序的对象将是大写字母的前n个字符。m表示将在这个问题实例中给出的A < B的关系的数量。接下来是m行,每个行包含一个这样的关系,包含三个字符:大写字母、字符“<”和第二个大写字母。没有字母会超出字母表前n个字母的范围。n = m = 0表示输入结束。
输出:(1)当可以确定唯一的排列的序列时输出“Sorted sequence determined after K relations: yyyy.”K表示在输入第几组关系之后可以确定序列,yyyy代表确定的序列。
(2)当序列次序之间有矛盾时输出“Inconsistency found after K relations.”
(3)序列不能判断是否有序时输出“Sorted sequence cannot be determined.”
题解:而这三种形式的判断是有顺序的:先判断是否有环(3),再判断是否有序(1),最后才能判断是否能得出结果(2)。注意:对于(2)必须遍历完整个图,而(1)和(3)一旦得出结果,对后面的输入就不用做处理了。题目处理的情况很多,所以需要边输入边处理。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

const int inf=0x3f3f3f3f;
const int N=30;
int mapp[N][N];
int deg[N];
int temp[N];
int topsort(int n)
{
    int a[N],ans=0,flag=1;
    //要使用很多次入度数组,所以使用临时数组a
    for (int i=1;i<=n;i++)
        a[i]=deg[i];
    for (int i=1;i<=n;i++)
    {
        int k=0,now=0;
        for (int j=1;j<=n;j++)
        {
            if (a[j]==0)//查找入度为零的顶点个数
            {
                k++;
                now=j;
            }
        }
        if (k==0)
            return 0;//有环
        if (k>1)//不直接返回-1,是因为可能还有环没判断出来
            flag=-1;
        temp[ans++]=now;
        a[now]=-1;
        for (int j=1;j<=n;j++)
            if(mapp[now][j]==1)
                a[j]--;
    }
    return flag;
}
int main()
{
    int n,m,flag;//当flag==1时,已经得出结果了
    char s[5];
    while (scanf("%d %d",&n,&m)&&(n||m))
    {
        flag=0;
        memset(mapp,0,sizeof(mapp));
        memset(deg,0,sizeof(deg));
        for (int i=1; i<=m; i++)
        {
            scanf("%s",s);
            if (flag)//一旦得出结果,对后续的输入不做处理
                continue;
            int x=s[0]-'A'+1;
            int y=s[2]-'A'+1;
            mapp[x][y]=1;
            deg[y]++;
            int cnt=topsort(n);
            if(cnt==0) //有环
            {
                printf("Inconsistency found after %d relations.\n",i);
                flag=1;
            }
            if(cnt==1) //有序
            {
                printf("Sorted sequence determined after %d relations: ",i);
                for(int j=0; j<n; j++)
                    printf("%c",temp[j]+'A'-1);
                printf(".\n");
                flag=1;
            }
        }
        if (!flag)
            printf("Sorted sequence cannot be determined.\n");
    }
    return 0;
}

确定比赛名次(HDU 1285)
题意:中文题意不解释。
题解:拓扑排序模板题

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

const int inf=0x3f3f3f3f;
const int N=500+7;
int mapp[N][N];
int deg[N];
void topsort(int n)
{
    int k=0;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            //找到前趋为0的顶点,既入度为0的顶点,并输出
            if (deg[j]==0)
            {
                printf("%d%c",j,i==n?'\n':' ');
                deg[j]--;//去掉这个点,入度为-1
                k=j;//记录这个点
                break;
            }
        }
        for (int j=1;j<=n;j++)
        {
            if (mapp[k][j]==1)
            {
                mapp[k][j]=0;//去掉以这个点为出度的边
                deg[j]--;//并把边的终点的入度-1
            }
        }
    }
}
int main()
{
    int n,m;
    while (~scanf("%d %d",&n,&m))
    {
        int u,v;
        memset(mapp,0,sizeof(mapp));
        memset(deg,0,sizeof(deg));
        for (int i=0;i<m;i++)
        {
            scanf("%d %d",&u,&v);
            if (!mapp[u][v])//防止有重边
            {
                mapp[u][v]=1;
                deg[v]++;//v的入读+1
            }
        }
        topsort(n);
    }
    return 0;
}

最短路(HDU 2544)
题意:中文题意不解释。
题解:题意里说的很清楚,就是求A到B之间的最短路。最短路的源点是A,源点只有一个,所以用dijkstra就行,顶点个数少于100,用floyd也可以。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=100+7;
int v[N],dis[N],map[N][N];
int dijstra(int s,int n)
{
    int ans=0,min;
    memset(v,0,sizeof(v));
    memset(dis,0,sizeof(dis));
    for (int i=1; i<=n; i++)
        dis[i]=map[s][i];
    v[s]=1;
    dis[s]=0;
    for (int i=1; i<=n; i++)
    {
        min=INF;
        for (int j=1; j<=n; j++)
        {
            if (!v[j]&&min>dis[j])
            {
                ans=j;
                min=dis[j];
            }
        }
        v[ans]=1;
        for (int j=1; j<=n; j++)
            if (!v[j]&&dis[j]>dis[ans]+map[ans][j])
                dis[j]=dis[ans]+map[ans][j];
    }
    return dis[n];
}
int main()
{
    int u,v,w;
    int n,m;
    while (~scanf("%d %d",&n,&m)&&(n||m))
    {
        memset(map,INF,sizeof(map));
        for (int i=0; i<m; i++)
        {
            scanf("%d %d %d",&u,&v,&w);
            //避免重边,保留权值最小的边
            if (map[u][v]>w)
                map[u][v]=map[v][u]=w;
        }
        int coun=dijstra(1,n);
        printf("%d\n",coun);
    }
    return 0;
}

畅通工程续(HDU 1874)
题意:中文题意不解释。
题解:求x到y之间的最短路。最短路的源点是x,源点只有一个,所以用dijkstra就行,顶点个数有200个,用floyd可能会超时。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=200+7;
int v[N],dis[N],map[N][N];
int x,y;
int n,m;//x表示起点,y表示终
int dijstra()
{
    int ans=0,min;
    memset(dis,INF,sizeof(dis));
    memset(v,0,sizeof(v));
    for (int i=0; i<n; i++)
        dis[i]=map[x][i];
    v[x]=1;
    dis[x]=0;
    for (int i=1; i<=n; i++)
    {
        min=INF;
        ans=0;
        for (int j=0; j<n; j++)
        {
            if (!v[j]&&min>dis[j])
            {
                ans=j;
                min=dis[j];
            }
        }
        v[ans]=1;
        for (int j=0; j<n; j++)
        {
            if (!v[j]&&dis[j]>dis[ans]+map[ans][j])
                dis[j]=dis[ans]+map[ans][j];
        }
    }
    return dis[y];
}
int main()
{
    int u,v,w;

    while (~scanf("%d %d",&n,&m)&&(n||m))
    {
        memset(map,INF,sizeof(map));
        for (int i=0; i<m; i++)
        {
            scanf("%d %d %d",&u,&v,&w);
            //避免重边,保留权值最小的边
            if (map[u][v]>w)
                map[u][v]=map[v][u]=w;
        }
        scanf("%d %d",&x,&y);
        int coun=dijstra();
        if (coun<INF)
            printf("%d\n",coun);
        else
            printf("-1\n");
    }
    return 0;
}

Six Degrees of Cowvin Bacon(POJ 2139)
题意:一群牛,最近在拍电影,他们准备玩Six Degrees of Cowvin Bacon的游戏。每头奶牛被认为与自己的距离是0度,如果两个牛在一起演电影,则认为两头牛之间的距离为1度,如果两头牛没有在一起拍过电影,但是他们和第三头牛一起拍过电影,则两头牛之间的距离为2度,比如A和B一起拍过电影,B和C一起拍过电影,则A和B之间的距离为2度。以此类推。现在要找到哪头牛与其他牛之间平均度最小。
输入:n和m,n表示牛的头数,标号从1开始,m表示电影的部数。下面m行为每部电影参加的牛的头数以及参加电影的牛的标号。
输出:最小的平均的度数(乘以100)。
题解:这道题的题意理解起来不太好理解,看不懂题在说什么。可以换一个角度,来分析这道题。如果A和B一起拍过电影就可以认为A和B之间的距离为1;A和B一起拍过电影,B和C一起拍过电影,则可以认为A和C之间不可达,A和B之间的距离为1,C和B之间的距离为1,A和C通过点B连接,A-B-C得到A-C=3;A和B一起拍过电影,B和C一起拍过电影,C和D一起拍过电影,则B-C=3,A-D=4;以此类推。这样就可以转化成一道最短路问题,求顶点到其余点之间最短路的平均距离,最小的平均距离*100并输出。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int Max=0x3f3f3f3f;
const int N=300+7;
int v[N],dis[N],map[N][N];
void floyd(int n)
{
    for (int k=1; k<=n; k++)
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)
                if (map[i][j]>map[i][k]+map[k][j])
                    map[i][j]=map[i][k]+map[k][j];
}
int main()
{
    int n,m,t,a;
    scanf("%d %d",&n,&m);
    memset(map,Max,sizeof(map));
    while(m--)
    {
        memset(dis,0,sizeof(dis));
        scanf("%d",&t);
        for (int j=0; j<t; j++)
        {
            scanf("%d",&a);
            dis[a]=1;//标记哪些参演了电影
        }
        //如果两头牛同演过一部电影,则两头之间的路径长度为1,
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)
                if (i==j)
                    map[i][j]=0;
                else if (dis[i]&&dis[j])
                    map[i][j]=map[j][i]=1;
    }//所有的电影输入结束之后,map数组里存的是所有直接可达的边,既度为1的关系
    //用二维数组做最短路的题,还没开始找最短路之前的map数组存的就是直接可达的两点之间的距离
    floyd(n);//求的人任意两点之间的最短路
    int sum;
    dis[0]=0;//此时的dis数组用来存放的是顶点i到其余个点之间最短路的和
    for (int i=1; i<=n; i++)
    {
        sum=0;
        for (int j=1; j<=n; j++)
            if (map[i][j]!=Max)
                sum+=map[i][j];
        dis[i]=sum;
    }
    sort(dis,dis+n+1);//排序找出最短距离之和的最小值
    printf("%d",dis[1]*100/(n-1));//避免精度问题先*100再求平均值
    return 0;
}

Cow Contest (POJ 3660)
题意:一群牛在比赛,每个回合可以确定两头牛之间的胜负关系。给出部分牛之间的胜负关系。保证牛与牛之间的名次不会出现矛盾。现在要给这些牛排个名次,问有几头牛的名次是可以确定的。
输入:n和m,n表示有n头牛参加比赛,m表示m个回合,下面m行,A B 表示A为获胜者。
输出:可以确定名次的奶牛的头数。
题解: floyd算法解决传递闭包问题。(A->B表示A战胜了B)假设只有三头牛,进行了两个回合的比赛,A->B,B->C,由这个回合的比赛可以得出确定的名词A->B->C,可以间接的认为A和C之间进行了比赛,转化成最短路的话就是,把每头牛看成一个顶点,每个回合的胜负关系看成一条有向路,A->B之间有路,B->C之间有路,则A->C之间有路

if (map[i][k]==1&&map[k][j]==1) 
 map[i][j]=1

A->B,A->C,则B与C之间的名次不能确定。

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
const int N=110;
int c[N][N];
int coun[N];
int n,m;
void flord()
{
    for (int k=1; k<=n; k++)
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)
                if (c[i][k]&&c[k][j])
                    c[i][j]=1;
}
int main()
{
    int a,b;
    while (~scanf("%d %d",&n,&m))
    {
        memset(c,0,sizeof(c));
        memset(coun,0,sizeof(coun));
        for (int i=0; i<m; i++)
        {
            scanf("%d %d",&a,&b);
            c[a][b]=1;
        }
        flord();
        for (int i=1; i<=n; i++)
        {
            for (int j=1; j<=n; j++)
            {
                if (c[i][j])
                {
                    coun[i]++;
                    coun[j]++;
                }
            }
        }
        int ans=0;
        for (int i=1; i<=n; i++)
            if (coun[i]==n-1)
                ans++;
        printf("%d\n",ans);
    }
    return 0;
}

Wormholes(POJ 3259)
题意: John 在探索自己农场的时候发现了一种神奇的虫洞,这种虫洞是单向的,这个虫洞可以回到过去,有N个农场,编号1….n,有M条路经,有W个虫洞。John想知道,从某个农场开始,走一些路和虫洞,可以回到他出发前或者见到他自己。
输入:有F组数据,每组数据有N个农场,编号1….n,有M条路经,有W个虫洞,路径是双向的,虫洞为单向的。
输出:YES或者NO。
题解:虫洞,可以回到过去,可以把虫洞看成路径长度为负数的路,这道题就成了判断途中是否存在负环的题了。Bellman ford 模板题。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

int n,m,w;
const int INF=0x3f3f3f3f;
const int N=5500;
struct nodenum
{
    int start;
    int end;
    int time;
} edge[N];

int num;
void addedge(int u,int v,int w)
{
    edge[num].start=u;
    edge[num].end=v;
    edge[num].time=w;
    num++;
}

int dis[505];

int Bellman_ford()
{
    for (int i=1; i<=n; i++)
        dis[i]=INF;
    dis[1]=0;
    for (int i=1; i<n; i++)
    {
        for (int j=0; j<num; j++)
        {
            if (dis[edge[j].end]>dis[edge[j].start]+edge[j].time)
            {
                dis[edge[j].end]=dis[edge[j].start]+edge[j].time;
            }
        }
    }
    for (int j=0; j<num; j++)
    {
        if (dis[edge[j].end]>dis[edge[j].start]+edge[j].time)
            return 0;
    }
    return 1;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int a,b,c;
        scanf("%d %d %d",&n,&m,&w);
        num=0;
        for (int i=1; i<=m; i++)
        {
            scanf("%d %d %d",&a,&b,&c);
            addedge(a,b,c);
            addedge(b,a,c);
        }
        for (int i=1; i<=w; i++)
        {
            scanf("%d %d %d",&a,&b,&c);
            addedge(a,b,-c);
        }
        if (!Bellman_ford())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

Silver Cow Party(POJ 3268)
题意:有N个农场(1≤N≤1000)编号1 . .N,现在有牛从农场出发去参加大牛党在农场举X(1≤X≤N)。总共M(1<=M<=100000)条单向路,一条路的时间是Ti
每头母牛都必须步行去参加聚会,聚会结束后,就回到她的农场。每头奶牛都很懒,因此选择了最短时间的最佳路线。一头牛的返回路线可能与她最初的路线不同,因为道路是单向的。
求所有的奶牛中,一头奶牛走到聚会地点和回来的时间最长是多少?
输入:第1行:三个空格分隔的整数,分别是N、M和X
第2行到第M+1:线空格分隔的整数来描述道路 i : Ai、Bi 和 Ti 。农场 Ai到Bi农场,需要Ti时间。
输出:一头奶牛走到聚会地点和回来的最长时间。
题解:因为路是单向路,要求N-1个农场到X的农场的最短路不好求,要循环n-1次,时间复杂度太高了。所以可以交换map[i][j]与map[j][i]的值,把求每个农场到农场X的最短路转化成求农场x到其余农场的最短路,变成求单源最短路。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
int map[1010][1010];
int dis[1010];
int logo[1010];
int n,m,x;
const int MAX=0x3f3f3f3f;
using namespace std;
void dijkstra( )
{
    int now;
    int min;
    memset(dis,MAX,sizeof(dis));
    memset(logo,0,sizeof(logo));
    for (int i=1; i<=n; i++)
    {
        dis[i]=map[x][i];
    }
    dis[x]=0;
    logo[x]=1;
    for (int i=1; i<=n; i++)
    {
        min=MAX;
        for (int j=1; j<=n; j++)
            if (logo[j]==0&&min>dis[ j ])
            {
                min=dis[ j ];
                now=j;
            }
        logo[now]=1;
        for (int j=1; j<=n; j++)
            if (logo[j]==0&&dis[ j ]>dis[now]+map[now][j])
                dis[ j ]=dis[now]+map[now][j];
    }
}
int main()
{
    int a,b,t,way[1010];
    scanf("%d %d %d",&n,&m,&x);

    memset(map,MAX,sizeof(map));
    for (int i=1; i<=m; i++)
    {
        scanf("%d %d %d",&a,&b,&t);
        map[a][b]=t;
    }
    //先找到从农场X到其余农场的最短路
    //既,每头牛参加完聚会回家的最短路
    dijkstra( );
    //把回家的最短路用另外一个数组存起来
    for (int i=1; i<=n; i++)
        way[i]=dis[i];
    //交换map[i][j]与map[j][i]的值之后
    //可以把求每个农场到农场X的最短路转化成求农场x到其余农场的最短路
    for (int i=1; i<=n; i++)
        for (int j=i+1; j<=n; j++)
        {
            t=map[i][j];
            map[i][j]=map[j][i];
            map[j][i]=t;
        }

    dijkstra( );
    t=way[1]+dis[1];
    //循环找最大值
    for (int i=2; i<=n; i++)
        if (way[i]+dis[i]>t)
            t=way[i]+dis[i];
    printf("%d\n",t);
    return 0;
}
    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/qq_39535750/article/details/81485595
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞