单源最短路建图问题

 

一.热浪

德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!!

他们的德克萨斯长角牛吃起来不错,可是它们并不是很擅长生产富含奶油的乳制品。

农夫John此时身先士卒地承担起向德克萨斯运送大量的营养冰凉的牛奶的重任,以减轻德克萨斯人忍受酷暑的痛苦。

John已经研究过可以把牛奶从威斯康星运送到德克萨斯州的路线。

这些路线包括起始点和终点一共有 T 个城镇,为了方便标号为 1到 T

除了起点和终点外的每个城镇都由 双向道路 连向至少两个其它的城镇。

每条道路有一个通过费用(包括油费,过路费等等)。

给定一个地图,包含 C 条直接连接 2 个城镇的道路

每条道路由道路的起点 Rs,终点 Re 和花费 Ci 组成。

求从起始的城镇 Ts 到终点的城镇 Te 最小的总费用。

输入格式

第一行: 4 个由空格隔开的整数: T,C,Ts,Te;

第 22 到第 C+1 行: 第 i+1行描述第 i 条道路,包含 3 个由空格隔开的整数: Rs,Re,Ci

输出格式

一个单独的整数表示从 Ts 到 Te 的最小总费用。

数据保证至少存在一条道路。

数据范围

1≤T≤2500,   (点数)
1≤C≤6200,   (变数)
1≤Ts,Te,Rs,Re≤T
1≤Ci≤1000

输入样例:

7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1

输出样例:

7

AC:代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2510, M = 6200 * 2 + 10;
int n,m,S,T;
int h[N],e[M],w[M],ne[M],idx;
int dist[N];

void add(int a,int b,int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx ++;
}
void bellman()
{
    memset(dist,0x3f,sizeof dist);
    dist[S] = 0;
    for(int op = 1 ; op <= n-1 ; op++)
    {
        bool flag = false;
        for(int x = 1; x <= n ; x++)
        {
            for(int i = h[x] ; i!=-1 ; i = ne[i])
            {
                int j = e[i];
                if(dist[j] > dist[x] + w[i])
                {
                    dist[j] = dist[x] + w[i];
                    flag = true;
                }
            }
            
        }
        if(!flag) break;
    }
    
    
    
}
int main()
{
    cin >> n >> m >>S >> T;
    memset(h,-1,sizeof h);
    for(int i = 0 ; i < m ; i++)
    {
        int a,b,c;
        cin >>a >> b >> c;
        add(a,b,c),add(b,a,c);
    }
    
    bellman();
    
    cout << dist[T];
    return 0;
    
}

 二.信使

战争时期,前线有 n 个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。

信使负责在哨所之间传递信息,当然,这是要花费一定时间的(以天为单位)。

指挥部设在第一个哨所。

当指挥部下达一个命令后,指挥部就派出若干个信使向与指挥部相连的哨所送信。

当一个哨所接到信后,这个哨所内的信使们也以同样的方式向其他哨所送信。信在一个哨所内停留的时间可以忽略不计

直至所有 n 个哨所全部接到命令后,送信才算成功。

因为准备充足,每个哨所内都安排了足够的信使(如果一个哨所与其他 k个哨所有通信联系的话,这个哨所内至少会配备 k 个信使)。

现在总指挥请你编一个程序,计算出完成整个送信过程最短需要多少时间。

输入格式

第 1 行有两个整数 n 和 m,中间用 1 个空格隔开,分别表示有 n 个哨所和 m 条通信线路。

第 2 至 m+1 行:每行三个整数 i、j、k 中间用 1 个空格隔开,表示第 i 个和第 j 个哨所之间存在 双向 通信线路,且这条线路要花费 k 天。

输出格式

一个整数,表示完成整个送信过程的最短时间。

如果不是所有的哨所都能收到信,就输出-1。

数据范围

1≤n≤100
1≤m≤200
1≤k≤1000

输入样例:

4 4
1 2 4
2 3 7
2 4 1
3 4 6

输出样例:

11

 广播问题:对于每个点来说,他接受到信件的时间等于指挥部到该点的最短距离。

#include<bits/stdc++.h>
using namespace std;
const int N = 110 , INF = 0x3f3f3f3f;
int n,m;
int d[N][N];

int main()
{
    
    cin >> n >> m;
    memset(d,0x3f,sizeof d);
    for(int i = 1 ; i<=n;i++) d[i][i] = 0;
    for(int i = 0 ; i < m ; i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        d[a][b] = d[b][a] = min(d[a][b],c);
    }
    
    for(int k = 1 ; k <= n ; k++)
        for(int i = 1 ; i<=n;i++)
            for(int j = 1 ; j <= n ;j++)
                d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
    
    int res = 0;
    for(int i=1; i<=n;i++)
        if(d[1][i]==INF)
        {
            res = -1;
            break;
        }
        else res = max(res,d[1][i]);
    
    cout << res;
    return 0;
}

bellman

#include<bits/stdc++.h>
using namespace std;
const int N = 110,M = 410;
int n,m;
int d[N],h[N],w[M],ne[M],e[M],idx;

void add(int a,int b,int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}
int bell()
{
    memset(d,0x3f,sizeof d);
    d[1] = 0;
    for(int op=1 ; op <= n-1 ; op++)
    {
        bool flag = false;
        for(int x = 1 ; x <= n ; x++)
        {
            for(int i = h[x] ; i!= -1 ; i = ne[i])
            {
                int j = e[i];
                if(d[j] > d[x] + w[i])
                {
                    d[j] =  d[x] + w[i];
                    flag = true;
                }
            }
        }
        if(!flag) break;
    }
    
    int res = 0;
    for(int i = 1 ; i <= n ; i++)
    {
        res = max(res,d[i]);
    }
    if(res == 0x3f3f3f3f) return -1;
    else return res;
}
int main()
{
    memset(h,-1,sizeof h);
    cin >> n >> m;
    while(m--)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c);
        add(b,a,c);
    }
    
    cout << bell();
}

三.香甜的黄油

农夫John发现了做出全威斯康辛州最甜的黄油的方法:糖。

把糖放在一片牧场上,他知道 N 只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。

当然,他将付出额外的费用在奶牛上。

农夫John很狡猾,就像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。

他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。

农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。

给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。

数据保证至少存在一个牧场和所有牛所在的牧场连通

输入格式

第一行: 三个数:奶牛数 N,牧场数 P,牧场间道路数 C。

第二行到第 N+1 行: 1 到 N 头奶牛所在的牧场号。

第 N+2 行到第 N+C+1 行:每行有三个数:相连的牧场A、B,两牧场间距 D,当然,连接是双向的。

输出格式

共一行,输出奶牛必须行走的最小的距离和。

数据范围

1≤N≤500,
2≤P≤800,(结点数量)
1≤C≤1450,(边的数量)
1≤D≤255,

输入样例:

3 4 5
2
3
4
1 2 1
1 3 5
2 3 7
2 4 3
3 4 5

输出样例:

8

  

#include<bits/stdc++.h>
using namespace std;
const int N = 810,M = 3000,INF = 0x3f3f3f3f;
int n,p,m; 
int id[N];
int h[N],e[M],ne[M],w[M],idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

int spfa(int start)
{
    memset(dist,0x3f,sizeof dist);
    dist[start] = 0;
    queue<int> q;
    q.push(start);
    st[start] = true;
    
    while(q.size())
    {
        int t = q.front();
        q.pop();
        st[t] = false;
        
        for(int i = h[t] ; i != -1 ; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if(!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
        
    }
    
    int res = 0;
    for(int i = 0 ; i < n ; i++)
    {
        int j = id[i];
        if(dist[j] == INF)  return INF;
        else
            res += dist[j];
    }
    return res;
}
int main()
{
    cin >> n >> p >> m;
    for(int i = 0 ; i < n ; i++)
        cin >> id[i];
    memset(h,-1,sizeof h);
    for(int i = 0 ; i < m ; i++)
    {
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c),add(b,a,c);
    }
    int res = INF;
    for(int i = 1 ; i <= p ;  i++)
        res = min(res,spfa(i));
    cout << res << endl;
    
    return 0;
}

四.最小花费

在 n 个人中,某些人的银行账号之间可以互相转账。

这些人之间转账的手续费各不相同。

给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 A 最少需要多少钱使得转账后 B 收到 100 元。

输入格式

第一行输入两个正整数 n,m,分别表示总人数和可以互相转账的人的对数。

以下 m 行每行输入三个正整数 x,y,z,表示标号为 x 的人和标号为 y 的人之间互相转账需要扣除 z% 的手续费 ( z<100 )。

最后一行输入两个正整数 A,B。

数据保证 A 与 B 之间可以直接或间接地转账。

输出格式

输出 A 使得 B 到账 100 元最少需要的总费用。

精确到小数点后 8 位。

数据范围

1≤n≤2000,
m≤1e5

输入样例:

3 3
1 2 1
2 3 2
1 3 3
1 3

输出样例:

103.07153164

 AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int n,m,S,T;
double g[N][N];
double d[N];
bool st[N];
void dijkstra()
{
    d[S] = 1;  //乘法的零元是1
    for(int i = 1 ; i <= n ; i++)
    {
        int t = -1;
        for(int j = 1 ; j <= n ; j++)
            if(!st[j] && (t==-1 || d[j] > d[t]))   //最长路问题找到最大点
                t = j;
        st[t] = true;
        for(int j = 1;  j <= n ; j++)
            d[j] = max(d[j],d[t] * g[t][j]);   //取最大值更新
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        double w = (100.0-c)/100;
        g[a][b] = g[b][a] = max(g[a][b],w);   //取最大值
        
    }
    cin >> S >>T;
    dijkstra();
    
    printf("%.8lf",100.0/d[T]);
}

spfa

//邻接矩阵
#include <bits/stdc++.h>

using namespace std;

const int N = 2010;

int n, m, S, T;
double g[N][N];
double dist[N];
bool st[N];

void spfa()
{
    dist[S] = 1;
    queue<int> q;
    q.push(S);
    st[S]=true;
    while(q.size()){
         int t = q.front();
        q.pop();

        st[t] = false;
       for (int i = 1; i <= n; i ++ ){
           if(dist[i]<dist[t]*g[t][i]){
               dist[i]=dist[t]*g[t][i];
               if(!st[i]){
                   q.push(i);
                   st[i]=true;
               }
           }
       }
    }
    
   
}

int main()
{
    scanf("%d%d", &n, &m);

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        double z = (100.0 - c) / 100;
        g[a][b] = g[b][a] = max(g[a][b], z);
    }

    cin >> S >> T;

    spfa();

    printf("%.8lf\n", 100 / dist[T]);

    return 0;
}

//邻接表
#include <bits/stdc++.h>

using namespace std;

const int N = 2010,M=200010;

int n, m, S, T;
int h[N], e[M], ne[M], idx;
double dist[N],w[M];
bool st[N];
void add(int a, int b, double c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void spfa()
{
    dist[S] = 1;
    queue<int> q;
    q.push(S);
    st[S]=true;
    while(q.size()){
         int t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
           if(dist[j]<dist[t]*w[i]){
               dist[j]=dist[t]*w[i];
               if(!st[j]){
                   q.push(j);
                   st[j]=true;
               }
           }
       }
    }
   
}

int main()
{
     memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m);

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        double z = (100.0 - c) / 100;
        add(a,b,z);
        add(b,a,z);
    }

    cin >> S >> T;

    spfa();

    printf("%.8lf\n", 100 / dist[T]);

    return 0;
}

五.最优乘车

H 城是一个旅游胜地,每年都有成千上万的人前来观光。

为方便游客,巴士公司在各个旅游景点及宾馆,饭店等地都设置了巴士站并开通了一些单程巴士线路。

每条单程巴士线路从某个巴士站出发,依次途经若干个巴士站,最终到达终点巴士站。

一名旅客最近到 H 城旅游,他很想去 S 公园游玩,但如果从他所在的饭店没有一路巴士可以直接到达 S 公园,则他可能要先乘某一路巴士坐几站,再下来换乘同一站台的另一路巴士,这样换乘几次后到达 S 公园。

现在用整数 1,2,…N给 H 城的所有的巴士站编号,约定这名旅客所在饭店的巴士站编号为 1,S 公园巴士站的编号为 N。

写一个程序,帮助这名旅客寻找一个最优乘车方案,使他在从饭店乘车到 SS 公园的过程中换乘的次数最少。

输入格式

第一行有两个数字 M 和 N,表示开通了M 条单程巴士线路,总共有 N 个车站

从第二行到第M+1 行依次给出了第 1 条到第 M 条巴士线路的信息,其中第 i+1 行给出的是第 i 条巴士线路的信息,从左至右按运行顺序依次给出了该线路上的所有站号,相邻两个站号之间用一个空格隔开。

输出格式

共一行,如果无法乘巴士从饭店到达 S 公园,则输出 NO,否则输出最少换乘次数,换乘次数为 0 表示不需换车即可到达。

数据范围

1≤M≤100,
2≤N≤500

输入样例:

3 7
6 7
4 7 3 6
2 1 3 5

输出样例:

2

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 510;
bool g[N][N];
int m,n;
int dist[N],stop[N];
bool st[N];
void bfs()
{
    queue<int> q;
    q.push(1);
    st[1] = true;
    memset(dist,0x3f3f3f3f,sizeof dist);
    dist[1] = 0;
    while(q.size())
    {
        int t = q.front();
        q.pop();
        
        for(int i = 1 ; i <= n ; i++)
        {
            if(g[t][i] && !st[i])
            {
                q.push(i);
                st[i] = true;
                dist[i] = dist[t] + 1;
            }
            
        }
        
    }
}
int main()
{
    cin >> m >> n;
    string line;
    getline(cin,line);
    while(m--)
    {
        getline(cin,line);
        stringstream ssin(line);
        int cnt = 0,p;
        while(ssin>>p) stop[cnt++] = p;
        for(int j = 0 ; j < cnt ; j++)
            for(int k = j ; k<cnt ; k++)
                g[stop[j]][stop[k]] = true;
    }
    
    bfs();
    if(dist[n]==0x3f3f3f3f)  cout << "NO";
    else cout << max(dist[n]-1,0);
    return 0;
}

 

六.昂贵的聘礼

年轻的探险家来到了一个印第安部落里。

在那里他和酋长的女儿相爱了,于是便向酋长去求亲。

酋长要他用 10000个金币作为聘礼才答应把女儿嫁给他。

探险家拿不出这么多金币,便请求酋长降低要求。

酋长说:”嗯,如果你能够替我弄到大祭司的皮袄,我可以只要 8000金币。如果你能够弄来他的水晶球,那么只要 5000金币就行了。”

探险家就跑到大祭司那里,向他要求皮袄或水晶球,大祭司要他用金币来换,或者替他弄来其他的东西,他可以降低价格。

探险家于是又跑到其他地方,其他人也提出了类似的要求,或者直接用金币换,或者找到其他东西就可以降低价格。

不过探险家没必要用多样东西去换一样东西,因为不会得到更低的价格。

探险家现在很需要你的帮忙,让他用最少的金币娶到自己的心上人。

另外他要告诉你的是,在这个部落里,等级观念十分森严。

地位差距超过一定限制的两个人之间不会进行任何形式的直接接触,包括交易。

他是一个外来人,所以可以不受这些限制。

但是如果他和某个地位较低的人进行了交易,地位较高的的人不会再和他交易,他们认为这样等于是间接接触,反过来也一样。

因此你需要在考虑所有的情况以后给他提供一个最好的方案。

为了方便起见,我们把所有的物品从 1 开始进行编号,酋长的允诺也看作一个物品,并且编号总是 1。

每个物品都有对应的价格 P,主人的地位等级 L,以及一系列的替代品 Ti 和该替代品所对应的”优惠” Vi。

如果两人地位等级差距超过了 M,就不能”间接交易”。

你必须根据这些数据来计算出探险家最少需要多少金币才能娶到酋长的女儿。

输入格式

输入第一行是两个整数 M,N,依次表示地位等级差距限制和物品的总数。

接下来按照编号从小到大依次给出了 N 个物品的描述。

每个物品的描述开头是三个非负整数 P、L、X,依次表示该物品的价格、主人的地位等级和替代品总数。

接下来 X 行每行包括两个整数 T 和 V,分别表示替代品的编号和”优惠价格”。

输出格式

输出最少需要的金币数。

数据范围

1≤N≤100,
1≤P≤10000,
1≤L,M≤N,
0≤X<N

输入格式

1 4
10000 3 2
2 8000
3 5000
1000 2 1
4 200
3000 2 1
4 200
50 2 0

输出格式

5250

 

 

 题意:

某天超市搞活动,小明想买一个自己一直想买的电脑,平时需要7000,小明觉得太贵了。但活动当天,超市里的商品可以通过买其他商品获得优惠券。例如买一个键盘然后买电脑只需要5000,如果买一个鼠标买电脑只需要4000。同理,买其他商品也有这样的优惠活动。但商场出于某种目的,给每一件商品都标了等级,如果买了低于某个等级的商品,就不能够再买某些高级商品。
为了方便,商店将每一件商品从1开始编号,电脑的编号为1.每一件商品都有对应的价格p,等级l,以及所对应的优惠商品Ki和优惠价格Ri。如果两件商品等级差超过D,就不能同时购买。小明想用最少的钱买到电脑。

输入:

第一行是两个整数D,t(1 <= t <= 100),分别代表等级的差距限制和商品的数量。接下来按照编号从1到t给出商品的描述。每个商品的描述开头是v、e、m(m < t),表示该物品的价格、商品的等级和对应优惠商品总数。接下来m行每行包括两个整数K和R,分别表示优惠商品的编号和对应商品优惠后价格。

输出:最少需要的钱。

思路:

建立一个超级源点0,从0建立一条边到每个物品,权值为物品的价值。代表花费多少钱就可以购买这个物品。
若某个物品拥有替代品,代表从替代品建立一条边到这个物品,价值为替代的价值。 代表我有了这个替代品,那么还需要花费多少就能买这个物品。

最后就是等级制度。我们可以枚举每个等级区间,每次求最短路是只能更新在这个区间里面的物品。枚举所有情况求一个最小值就可以了。 特别注意的是区间必须包含1点。 那么范围就是【L[1] - m, L[1]】

 

 AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 110,INF = 0x3f3f3f3f;
int n,m; //n点数,m等级
int w[N][N],level[N];
int dist[N];
bool st[N];
int dij(int down,int up)
{
    memset(dist,0x3f,sizeof dist);
    memset(st,0,sizeof st);
    dist[0] = 0;
    for(int i = 0 ; i <= n ; i++)
    {
        int t = -1;
        for(int j = 0 ; j<= n ; j++)
        {
            if(!st[j] && (t==-1 || dist[t] > dist[j] ))
                t = j;
        }
        
        st[t] = true;
        for(int j = 0 ; j <= n ; j++)
        {
            if(level[j] >= down && level[j] <= up)
                dist[j] = min(dist[j],dist[t] + w[t][j]);
        }
        
        
    }
    
    return dist[1];
    
}
int main()
{
    cin >> m >> n;  //m为等级限制,n为结点总数
    memset(w,0x3f,sizeof w);  //初始化
    for(int i = 1 ; i<=n ; i++) w[i][i] = 0;
    
    for(int i = 1 ; i <= n ; i++)
    {
        int price,cnt;
        cin >> price >> level[i] >> cnt;  //价格,等级,数量
        w[0][i] = min(w[0][i],price);
        while(cnt--)
        {
            int id,cost;
            cin >> id >> cost;
            w[id][i] = min(w[id][i],cost);  //可以替换的物品,在加上花费
        }
    }
    int res = INF;
    for(int i = level[1] - m ; i <= level[1] ; i++)  res = min(res,dij(i,i+m));  //枚举每一个包含level[1]的区间
    
    cout << res << endl;
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值