《算法笔记上机实验指南》第4章 入门篇(2)10.4最短路径

本文介绍了Dijkstra算法和深度优先搜索(DFS)在解决图论问题中的应用,包括最短路径计算、网络流量优化和路径查找。通过实例展示了这两种算法如何在不同场景下解决问题,如最小生成树、最短路径和网络调度等。同时,代码示例详细解释了算法的实现过程,帮助读者理解其工作原理。
摘要由CSDN通过智能技术生成

A1003

#include<iostream>
using namespace std;

const int maxn=501;
const int INF=100000000;

int n,m,s,e;

bool inq[maxn]={false};

int G[maxn][maxn],d[maxn],weight[maxn],w[maxn],num[maxn];

void D(int s)
{
    d[s]=0;
    num[s]=1;
    w[s]=weight[s];
    for(int i=0; i<n; i++)
    {
        int u=-1,MIN=INF;
        for(int j=0; j<n; j++)
        {
            if(inq[j]==false && MIN>d[j])  //这个for是每次找当前的所有结点中值最小的,不是每次算上之前的,所以必须要判断
            {
                MIN=d[j];
                u=j;
            }
        }
        if(u==-1)
            return ;
        inq[u]=true;
        for(int v=0; v<n; v++)
        {
            if(inq[v]==false && G[u][v]!=INF)
            {
                if(d[v]>d[u]+G[u][v])
                {
                    d[v]=d[u]+G[u][v];
                    num[v]=num[u];
                    w[v]=w[u]+weight[v];
                }
                else if(d[v]==d[u]+G[u][v])
                {
                    if(w[v]<w[u]+weight[v])
                    {
                        w[v]=w[u]+weight[v];
                    }
                    num[v]+=num[u];
                }
            }
        }
    }
}

int main()
{
    cin >> n >> m >> s >> e;
    fill(G[0],G[0]+maxn*maxn,INF);
    fill(d,d+maxn,INF);
    for(int i=0; i<n; i++)
    {
        cin >> weight[i];
    }
    int temp1,temp2,temp3;
    for(int i=0; i<m; i++)
    {
        cin >> temp1 >> temp2 >> temp3;
        G[temp1][temp2]=temp3;
        G[temp2][temp1]=temp3;
    }
    D(s);
    cout << num[e] << " " << w[e];
    return 0;
}


A1018

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;

const int maxn=510;
const int INF=10000000;

int Cmax,n,Sp,m;

int weight[maxn],G[maxn][maxn],d[maxn],minRemain=INF,minNeed=INF;

vector<int> pre[maxn];

vector<int> path,tempPath;

bool inq[maxn]={false};

void D(int s)
{
    d[s]=0;
    for(int i=0; i<n; i++)
    {
        int u=-1,MIN=INF;
        for(int j=0; j<=n; j++)
        {
            if(inq[j]==false && d[j]<MIN)
            {
                MIN=d[j];
                u=j;
            }
        }
        if(u==-1)
            return ;
        inq[u]=true;
        for(int v=0; v<=n; v++)
        {
            if(inq[v]==false && G[u][v]!=INF)
            {
                if(d[v]>d[u]+G[u][v])
                {
                    d[v]=d[u]+G[u][v];
                    pre[v].clear();
                    pre[v].push_back(u);
                }
                else if(d[v]==d[u]+G[u][v])
                {
                    pre[v].push_back(u);
                }

            }

        }
    }
}

void DFS(int v)   //按照图中的例子是从下向上进行DFS,目的是通过前驱数组pre,来从有问题的站出发一直到PBMC
{
    if(v==0)
    {
        tempPath.push_back(v);
        int need=0,remain=0;
        for(int i=tempPath.size()-1; i>=0; i--)  //肯定是访问的这一条路上的权值,不是前驱数组
        {
            int id=tempPath[i];
            if(weight[id]>0)
                remain+=weight[id];
            else
            {
                if(remain>abs(weight[id]))
                    remain-=abs(weight[id]);
                else
                {
                    need+=abs(weight[id])-remain;
                    remain=0;
                }
            }
        }
        if(minNeed>need)
        {
            minNeed=need;
            path=tempPath;
            minRemain=remain;
        }
        else if(minNeed==need && minRemain>remain)
        {
            minRemain=remain;
            path=tempPath;
        }
        tempPath.pop_back();
        return ;
    }
    tempPath.push_back(v);
    for(int i=0; i<pre[v].size(); i++)  //相当于进入前驱结点再次调用DFS
    {
        DFS(pre[v][i]);
    }
    tempPath.pop_back();
}

int main()
{
    cin >> Cmax >> n >> Sp >> m;
    for(int i=1; i<=n; i++)
    {
        cin >> weight[i];
        weight[i]-=Cmax/2;
    }

    fill(G[0],G[0]+maxn*maxn,INF);
    fill(d,d+maxn,INF);
    int temp1,temp2,temp3;
    for(int i=0; i<m; i++)
    {
        cin >> temp1 >> temp2 >> temp3;
        G[temp1][temp2]=temp3;
        G[temp2][temp1]=temp3;
    }
    D(0);
    DFS(Sp);
    cout << minNeed << " ";
    for(int i=path.size()-1; i>=0; i--)
    {
        if(i!=path.size()-1)
            cout << "->";
        cout << path[i];
    }
    cout << " " << minRemain;
    return 0;
}


题目意思:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

A1030

D+DFS

#include<iostream>
using namespace std;

const int maxn=501;
const int INF=1000000000;

int G[maxn][maxn],d[maxn],c[maxn],cost[maxn][maxn],pre[maxn];

int n,m,s,e;

bool inq[maxn]={false};

void D(int s)
{
    d[s]=0;
    c[s]=0;
    for(int i=0; i<n; i++)
    {
        int u=-1,MIN=INF;
        for(int j=0; j<n; j++)
        {
            if(inq[j]==false && MIN>d[j])   //注意输入
            {
                MIN=inq[j];
                u=j;
            }
        }
        if(u==-1)
        {
            return ;
        }
        inq[u]=true;
        for(int v=0; v<n; v++)
        {
            if(inq[v]==false && G[u][v]!=INF)
            {
                if(d[v]>d[u]+G[u][v])
                {
                    d[v]=d[u]+G[u][v];
                    c[v]=c[u]+cost[u][v];
                    pre[v]=u;
                }
                else if(d[v]==d[u]+G[u][v])
                {
                    if(c[v]>c[u]+cost[u][v])
                    {
                        c[v]=c[u]+cost[u][v];
                        pre[v]=u;             //如果有更小的边权,需要重新统计前驱结点             
                    }

                }
            }
        }
    }
}

void DFS(int v)
{
    if(v==s)       //如果题中有起点用起点表示
    {
        cout << v << " ";
        return ;
    }
    DFS(pre[v]);
    cout << v << " ";
}

int main()
{
    cin >> n >> m >> s >> e;
    int temp1,temp2,temp3,temp4;
    fill(d,d+maxn,INF);
    fill(G[0],G[0]+maxn*maxn,INF);
    for(int i=0; i<m; i++)
    {
        cin >> temp1 >> temp2 >> temp3 >> temp4;
        G[temp1][temp2]=temp3;
        G[temp2][temp1]=temp3;
        cost[temp1][temp2]=temp4;
        cost[temp2][temp1]=temp4;
    }
    D(s);
    DFS(e);
    cout << d[e] << " " << c[e];
    return 0;
}

D

#include<iostream>
#include<vector>
using namespace std;

const int maxn=501;
const int INF=100000000;

int n,m,s,e;

int G[maxn][maxn],d[maxn],cost[maxn][maxn],c[maxn];
bool inq[maxn]={false};

int mincost=INF;

int pre[maxn];   //直接定义一个前驱数组


void D(int s)   //通过D来找到最短路径和最小边权以及用普通数组来记录的前驱结点
{
    d[s]=0;
    for(int i=0; i<n; i++)
    {
        int u=-1,MIN=INF;
        for(int j=0; j<n; j++)
        {
            if(inq[j]==false && d[j]<MIN)
            {
                MIN=d[j];
                u=j;
            }
        }
        if(u==-1)
            return ;
        inq[u]=true;
        for(int v=0; v<n; v++)
        {
            if(inq[v]==false && G[u][v]!=INF)
            {
                if(d[u]+G[u][v]<d[v])
                {
                    d[v]=d[u]+G[u][v];
                    c[v]=c[u]+cost[u][v];
                    pre[v]=u;         //令v的前驱是u
                }
                else if(d[u]+G[u][v]==d[v])
                {
                    if(c[v]>c[u]+cost[u][v])
                    {
                            c[v]=c[u]+cost[u][v];
                            pre[v]=u;              //如果能找到更小的边权还是令前驱为u
                    }

                }
            }
        }
    }
}

void DFS(int v)   //通过递归的方式输出路径
{
    if(v==s)
    {
        cout << v << " ";
        return ;
    }
    DFS(pre[v]);
    cout << v << " ";
}


int main()
{
    cin >> n >> m >> s >> e;
    int temp1,temp2,temp3,temp4;
    fill(d,d+maxn,INF);
    fill(G[0],G[0]+maxn*maxn,INF);
    for(int i=0; i<m; i++)
    {
        cin >> temp1 >> temp2 >> temp3 >> temp4;
        G[temp1][temp2]=temp3;
        G[temp2][temp1]=temp3;
        cost[temp1][temp2]=temp4;
        cost[temp2][temp1]=temp4;
    }
    D(s);
    DFS(e);
    cout << d[e] << " " << c[e];
    return 0;
}

1072

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

const int maxn=1024;
const int INF=100000004;

int G[maxn][maxn],d[maxn];

bool inq[maxn]={false};

int n,m,k,Ds;

void D(int s)
{
    fill(d,d+maxn,INF);
    memset(inq,false,sizeof(inq));      //上面这两个定义必须加上,只要调用D函数,就要重新定义
    d[s]=0;
    for(int i=0; i<n+m; i++)
    {
        int u=-1,MIN=INF;
        for(int j=1; j<=n+m; j++)
        {
            if(inq[j]==false && MIN>d[j])
            {
                MIN=d[j];
                u=j;
            }
        }
        if(u==-1)
            return ;
        inq[u]=true;
        for(int v=1; v<=n+m; v++)
        {
            if(inq[v]==false && G[u][v]!=INF)
            {
                if(d[v]>d[u]+G[u][v])
                {
                    d[v]=d[u]+G[u][v];
                }
            }
        }
    }
    d[s]=0;
}

int str(char s[])
{
    int id=0,len=strlen(s),i=0;       //strlen取字符串数组长度
    while(i<len)
    {
        if(s[i]!='G')
        {
            id=id*10+s[i]-'0';
        }
        i++;
    }
    if(s[0]=='G')
        return id+=n;
    else
        return id;
}

int main()
{
    cin >> n >> m >> k >> Ds;
    char temp1[5],temp2[5];
    int temp3;
    fill(G[0],G[0]+maxn*maxn,INF);
    for(int i=0; i<k; i++)
    {
        cin >> temp1 >> temp2 >> temp3;
        int id1=str(temp1);
        int id2=str(temp2);
        G[id1][id2]=temp3;
        G[id2][id1]=temp3;
    }
    D(1);
    double ansDis=-1,ansAvg=INF;   //所有的最短最大距离的最大值以及对应的平均值,如果前者相同,则要最小的品均值则设置为最大
    int ansId=-1;
    for(int i=n+1; i<=n+m; i++)    //排除距离>Ds  找出最短的最大距离  算出平均值
    {
        double minDis=INF,avg=0;
        D(i);
        for(int j=1; j<=n; j++)
        {
            if(d[j]>Ds)
            {
                minDis=-1;
                break;
            }
            if(minDis>d[j])
            {
                minDis=d[j];
            }
            avg+=1.0*d[j]/n;          //avg是double,d[j],n是int类型,所以前面*1.0
        }
        if(minDis==-1)          //表示加油站的距离超过了所给范围,所以不在这个位置建立加油站
            continue;
        if(minDis>ansDis)
        {
            ansDis=minDis;
            ansId=i;
            ansAvg=avg;
        }
        else if(minDis==ansDis && ansAvg>avg)
        {
            ansId=i;
            ansAvg=avg;
        }
    }
    if(ansId==-1)
        cout << "No Solution";
    else
    {
        printf("G%d\n",ansId-n);
        printf("%.1f %.1f",ansDis,ansAvg);    //printf这个保留一位小数是不进行四舍五入的
        //round(ansAvg*10)/10.0; 此方法可以对小数进行四舍五入保留1位小数,但是只对于小数,本题的要求也是保留1位小数,不用四舍五入
    }
    return 0;
}

A1087

#include<iostream>
#include<map>
#include<vector>
using namespace std;

const int maxn=300;
const int INF=1000000000;

int G[maxn][maxn],weight[maxn],d[maxn];

bool inq[maxn]={false};

int n,k,num=1,num1[maxn];

string S;

map<string,int> StringInt2;
map<int,string> IntString;

vector<int> pre[maxn];

vector<int> path,temppath;

int str(string s)
{
    if(StringInt2.find(s)!=StringInt2.end())
    {
        return StringInt2[s];
    }
    else
    {
        StringInt2[s]=num;
        IntString[num]=s;
        return num++;
    }
}

void D(int s)
{
    d[s]=0;
    num1[s]=1;
    for(int i=0; i<n; i++)
    {
        int u=-1,MIN=INF;
        for(int j=0; j<n; j++)
        {
            if(inq[j]==false && d[j]<MIN)
            {
                MIN=d[j];
                u=j;
            }
        }
        if(u==-1)
            return ;
        inq[u]=true;
        for(int v=0; v<n; v++)
        {
            if(inq[v]==false && G[u][v]!=INF)
            {
                if(d[v]>d[u]+G[u][v])
                {
                    d[v]=d[u]+G[u][v];
                    num1[v]=num1[u];
                    pre[v].clear();
                    pre[v].push_back(u);
                }
                else if(d[v]==d[u]+G[u][v])
                {
                    num1[v]+=num1[u];
                    pre[v].push_back(u);
                }
            }
        }
    }
}

int maxhappy=-1,maxav=-1;

void DFS(int v)
{
    if(v==StringInt2[S])
    {
        temppath.push_back(v);
        int sum=0,renshu=0,tempav;
        for(int i=temppath.size()-1; i>=0; i--)
        {
            int id=temppath[i];
            sum+=weight[id];
        }
        tempav=sum/(temppath.size()-1);
        if(maxhappy<sum)
        {
            maxhappy=sum;
            maxav=tempav;
            path=temppath;
        }
        else if(maxhappy==sum && tempav>maxav)
        {
            maxav=tempav;
            path=temppath;
        }
        temppath.pop_back();
        return ;
    }
    temppath.push_back(v);
    for(int i=0; i<pre[v].size(); i++)
        DFS(pre[v][i]);
    temppath.pop_back();
}

int main()
{
    cin >> n >> k >> S;
    fill(G[0],G[0]+maxn*maxn,INF);
    fill(d,d+maxn,INF);
    StringInt2[S]=0;
    IntString[0]=S;
    string temp1,temp2;
    int temp3;
    for(int i=0; i<n-1; i++)
    {
        cin >> temp1 >> temp3;
        int id=str(temp1);
        weight[id]=temp3;
    }
    for(int i=0; i<k; i++)
    {
        cin >> temp1 >> temp2 >> temp3;
        G[StringInt2[temp1]][StringInt2[temp2]]=temp3;
        G[StringInt2[temp2]][StringInt2[temp1]]=temp3;
    }
    D(StringInt2[S]);
    DFS(StringInt2["ROM"]);
    cout << num1[StringInt2["ROM"]] << " " << d[StringInt2["ROM"]] << " " << maxhappy << " " << maxav << endl;
    for(int i=path.size()-1; i>=0; i--)
    {
        if(i!=path.size()-1)
            cout << "->";
        cout << IntString[path[i]];
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值