最短路水题大合集

题目一:

Silver Cow Party

涉及知识点: 正反图的最短路


题意:

有N个农场 , 每个农场一头牛 , 现在他们要走到第X个农场去参加派对 , 然后返回各自的农场。

然后有M条单向路 , 每条路从A到B花费T

然后每头牛都会选择最短的路来走一个来回

求的是每头牛走的路中的最大值


思路:

图论水题 , 很裸的最短路算法。

算法采用spfa , 构造正反两个图 , 我们知道如果从x点开始走 , low[i]记录的就是从x出发到达i的最短距离。

那么去的时候采用正图,回的时候采用反图就可以了


代码如下:

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

typedef long long LL;
const int INF = 0x6fffffff;
const long long LLinf = 1e30;

int n,m,x;

int vis[1010];
int G[1010][1010] , G2[1010][1010];
int low[1010],low2[1010];

void init()
{
    memset(vis,0,sizeof(vis));
    for(int i = 0 ; i <= n ; i++)
    {
        low[i] = INF,low2[i] = INF;
        for(int j = 0 ; j <= n ; j++)
        {
            G[i][j] = G2[i][j] = INF;
        }
    }

}

void spfa(int src , int *low2 , int g[][1010])
{
    memset(vis , 0 , sizeof(vis));

    queue<int> que;
    que.push(src);
    low2[src] = 0;
    vis[src] = 1;

    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur] = 0;
        for(int i = 1 ; i <= n ; i++)
            if(low2[cur]+g[cur][i] < low2[i])
            {
                low2[i] = low2[cur]+g[cur][i];
                if(!vis[i])
                {
                    que.push(i);
                    vis[i]=1;
                }
            }
    }
}

int main()
{
    while(~scanf("%d%d%d",&n,&m,&x))
    {
        init();

        int a,b,t;
        for(int i = 0 ; i < m ; i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            G[a][b] = min(G[a][b] , t);
            G2[b][a] = min(G2[b][a] , t);
        }
        

        spfa(x , low , G);
        spfa(x , low2 , G2);

        int maxc = -1;
        for(int i = 1 ; i <= n ; i++)
        {
            if(low[i]!= INF && low2[i] != INF)
                maxc = max(low[i]+low2[i] , maxc);
        }

    cout << maxc << endl;

    }
    return 0;
}

题目二

Invitation Cards

题意:

很丢脸地没看懂题。。。百度的中文翻译,说是从1到所有点的最短路和加上所有点到1的最短路和,图是有向图


思路:

和上题如出一辙,于是无脑敲代码瞬间1Y了 , 就当是复习了一下邻接表


代码如下

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const long long LLinf = 1e30;

int N;

struct edge
{
    int now;
    int next;
    int w;
}e[1000000+10];

struct node
{
    int x,y,t;
}Node[1000000+10];

int cnt=0;
int head[1000000+10];
int low[1000000+10];
int vis[1000000+10];

void add(int x , int y , int v)
{
    e[cnt].now = y;
    e[cnt].next = head[x];
    e[cnt].w = v;
    head[x] = cnt++;
}

LL sum;
int P,Q;

void spfa(int src)
{
    memset(vis , 0 , sizeof(vis));


    for(int i = 0 ; i <= P ; i++)
        low[i] = INF;

    low[src]=0;
    vis[src]=1;
    queue<int> que;
    que.push(src);

    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur]=0;

        for(int i = head[cur] ; i!= -1 ; i = e[i].next)
        {
            int now = e[i].now;
            if(low[now] > low[cur] + e[i].w)
            {
                low[now] = low[cur] + e[i].w;

                if(!vis[now])
                {
                    que.push(now);
                    vis[now] =1;
                }
            }
        }
    }
}

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

    while(N--)
    {
        sum=0;

        scanf("%d%d" , &P , &Q);
        memset(head , -1 , sizeof(head));

        for(int i = 0 ; i < Q ; i++)
        {
            scanf("%d%d%d" , &Node[i].x,&Node[i].y,&Node[i].t);
            add(Node[i].x,Node[i].y,Node[i].t);
        }

        spfa(1);
        for(int i = 2 ; i <= P ; i++)
            sum += low[i];

        cnt=0;
        memset(head , -1 , sizeof(head));

        for(int i = 0 ; i < Q ; i++)
            add(Node[i].y,Node[i].x,Node[i].t);
        spfa(1);

        for(int i = 2 ; i <= P ; i++)
            sum += low[i];

        cout << sum << endl;
    }

    return 0;
}

题目三:

Stockbroker Grapevine

涉及知识点:不同起点的最短路,也可用最小生成树做


题意:

直接讲输入:

第一行一个数n,是总共有n个人

后面每一行的第一个数k:和k个人有单向关系;然后k对数,表示(有关系的人的编号,传输信息时间)

求从哪个人开始能够以最短时间使信息传遍所有人,输出这个人的编号和最短时间,始终不能传遍所有人输出dis...


思路:

对于一个人,首先在能传遍所有人的情况下,记到达某个人所花时间为tt,那么求tt肯定是按照最短时间去走的,最短路就是用在这了,求出的所有tt中的最大值就是传遍所有人需要的最短时间ttt。然后求出所有人的ttt判断哪个最小就可以了。

判断dis的方法:

我利用了low这个数组,如果在spfa结束后low[1]-low[n]中有inf,说明他没被更新过,是不可达的。


代码如下:

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int G[110][110];
int vis[110];
int low[110];
int n;

int spfa(int src)
{
    memset(vis,0,sizeof(vis));
    memset(low , 0x1f , sizeof(low));
    vis[src]=1;
    queue<int> que;
    que.push(src);
    low[src] = 0;

    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur]=0;

        for(int i = 1 ; i <= n ; i++)
        {
            if(low[i] > low[cur] + G[cur][i])
            {
                low[i] = low[cur]+G[cur][i];

                if(!vis[i])
                {
                    vis[i]=1;
                    que.push(i);
                }
            }
        }
    }

    if(*max_element(low+1 , low+n+1) == inf)
        return -1;
    //low[src] = inf;

    return *max_element(low+1 , low+n+1);
}

int main()
{
    while(~scanf("%d",&n)&&n)
    {
        memset(G , 0x1f , sizeof(G));
        for(int i = 1 ; i <= n ; i++)
        {
            int nn;
            scanf("%d" , &nn);

            for(int j = 0 ; j < nn ; j++)
            {
                int to,w;
                scanf("%d%d" , &to , &w);

                G[i][to] = w;
            }
        }

        int res = inf;
        int pos=-1;
        for(int i = 1 ; i <= n ; i++)
        {
            int ans = spfa(i);
            if(ans != -1 && ans < res)
                res = ans,pos = i;
        }

        if(res == inf)
            printf("disjoint\n");
        else
            printf("%d %d\n" , pos , res);
    }
    return 0;
}


题目四:

Heavy Transportation

涉及知识点:最短路变形,最小生成树变形


题意:

从1到n有若干段路,每条路的最大载重为w,在所有从1到n可达的路中找出一条路,使得能运送的货物最重,比如从1到n的其中一条路有3段路,最大载重为1,2,3,则最多能运送重为1的货物,即一个木桶原理模型。


思路:

这题我是用最小生成树变体水过的,思路很简单:肯定是要求选取的路中,每段路载重尽可能大,那么就相当于找一棵最大生成树,当然我们不必找全,从1出发,当n加入到已选点的集合时,就可以退出了


最小生成树代码如下:

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int G[1010][1010];
int vis[1010];
int low[1010];
int n,m;

int prim()
{
    int minc = inf;
    vis[1]=1;

    for(int i = 2 ; i <= n ; i++)
        low[i] = G[1][i];

    for(int i = 2 ; i <= n ; i++)
    {
        int maxc = -1;
        int pos=-1;

        for(int j = 1 ; j <= n ; j++)
            if(!vis[j] && low[j] > maxc)
            {
                maxc = low[j];
                pos=j;
            }

        vis[pos]=1;
        minc = min(minc,maxc);

        if(pos == n)
            return minc;

        for(int j = 1 ; j <= n ;j++)
            if(!vis[j] && low[j] < G[pos][j])
                low[j] = G[pos][j];
    }

    return minc;
}

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

    for(int ka = 1 ; ka <= t ; ka++)
    {
        memset(vis,0,sizeof(vis));
        memset(low , 0 , sizeof(low));
        memset(G,0,sizeof(G));
        scanf("%d%d" , &n,&m);

        for(int i = 0 ; i < m ; i++)
        {
            int x,y,w;
            scanf("%d%d%d" , &x , &y , &w);

            G[x][y] = G[y][x] = w;
        }

        printf("Scenario #%d:\n%d\n\n",ka,prim());
    }

    return 0;
}


后来补了个最短路的,代码如下:

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int G[1010][1010];
int vis[1010];
int low[1010];              //此时low表示从1到i的路段中最短路段的载重量
int n,m;

int dijkstra()
{
    int minc = inf;
    vis[1]=1;

    for(int i = 2 ; i <= n ; i++)
        low[i] = G[1][i];

    for(int i = 2 ; i <= n ; i++)
    {
        int maxc = -1;
        int pos=-1;

        for(int j = 1 ; j <= n ; j++)
            if(!vis[j] && low[j] > maxc)
            {
                maxc = low[j];
                pos=j;
            }

        vis[pos]=1;

        for(int j = 1 ; j <= n ;j++)
            if(!vis[j] && low[j] < min(G[pos][j],maxc)) //这里的maxc就是从1到pos的路段中最大的载重量
                low[j] = min(G[pos][j],maxc);
    }

    return low[n];
}

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

    for(int ka = 1 ; ka <= t ; ka++)
    {
        memset(vis,0,sizeof(vis));
        memset(low , 0 , sizeof(low));
        memset(G,0,sizeof(G));
        scanf("%d%d" , &n,&m);

        for(int i = 0 ; i < m ; i++)
        {
            int x,y,w;
            scanf("%d%d%d" , &x , &y , &w);

            G[x][y] = G[y][x] = w;
        }

        printf("Scenario #%d:\n%d\n\n",ka,dijkstra());
    }

    return 0;
}

题目五:

Frogger


题意:

没怎么仔细读就上手做了,一次CE小错误改正之后就过了,那估计题目是没理解错。

说是有一堆石头,两只青蛙在1,2两块石头上,1要通过其他石头跳到2。称从一块石头跳到另一块为一次跳跃,而每条路程中,一定会有一个跳跃距离是最大的,问所有最大跳跃的最短距离是多少。


思路:和上一题如出一辙,dijkstra变形水过


代码:

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

double G[210][210];
double low[210];                //这次是表示从1—i的最大跳跃距离
int vis[210];
int x[210],y[210];
int n;

double dis(int x1,int y1,int x2,int y2)
{
    return sqrt(1.0*(x1-x2)*(x1-x2) + 1.0*(y1-y2)*(y1-y2));
}

double dij()
{
    int ans = inf;
    for(int i = 2 ; i <= n ; i++)
    {
        low[i] = G[1][i];
    }

    for(int i = 2 ; i<= n ; i++)
    {
        int minc = inf;
        int pos = -1;
        for(int j = 1 ; j <= n ; j++)
        {
            if(!vis[j] && low[j] < minc)        //在未收入的点集中寻找最小的 "最大跳跃距离"
            {
                minc = low[j];
                pos = j;
            }
        }

        vis[pos]=1;
        ans = min(ans,minc);

        for(int j = 1 ; j <= n ; j++)
        {
            if(!vis[j] && low[j] > max(G[pos][j] , low[pos]))           //更新当前最大跳跃距离
            {
                low[j] = max(G[pos][j] , low[pos]);
            }
        }
    }

    return low[2];
}

int main()
{
    int t=0;
    while(~scanf("%d",&n) && n)
    {
        memset(G,0x1f,sizeof(G));
        memset(low,0x1f,sizeof(low));
        memset(vis,0,sizeof(vis));
        low[1]=0;
        vis[1]=1;

        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d%d",&x[i],&y[i]);

            for(int j = 1 ; j < i ; j++)
            {
                G[i][j] = G[j][i] = dis(x[i],y[i],x[j],y[j]);
            }
        }

        printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++t,dij());
    }
    return 0;
}


题目四和五,一个是最小中找最大,一个是最大中找最小,千万熟记机理以及过程!!


题目六:

昂贵的聘礼

中文题,题意略


思路:

一种物品在另一种物品的影响下会改变其价格

仅考虑3种物品:

我从1到3要10000元,但是如果有2,2是100元,1的价格就会变为200,也就是说1->2->3的价值和要比1->3小,我们显然会选前者。

从上例可看出这是一个典型的松弛操作,于是自然就想到用最短路解决。

建图:

G[i][j]表示在i的影响下j的价格,那么如果没有物品影响呢?找一个点(这里用0),G[0][i]表示i的初始价格,搜图从此点出发

松弛操作的限制条件:

就是等级限制了,这里是一群交易人的最大lv和最小lv之差不能超过m,而且酋长肯定是在其中的。

但是如果直接限定范围:[lv-m , lv+m] (注意酋长等级不一定是最大的),区间长度为2m肯定不可取,所以我们要以他为中心,枚举所有可能的等级区间[lv-m + i , lv + i]


代码如下:

其实写得挺挫的,根本没有必要保留一堆物品信息,仅保留等级就可以了

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int m,n;
int G[110][110];
int vis[110];
int low[110];

struct mono
{
    int price;
    int lv;
    int X;
    int replace[110][2];
}item[110];

void read()
{
    scanf("%d%d",&m,&n);
    memset(G,0x1f,sizeof(G));

    for(int i = 1 ; i <= n ; i++)
    {
        scanf("%d%d%d",&item[i].price,&item[i].lv,&item[i].X);

        G[0][i] = item[i].price;                       //G[0][i]表示毫无优惠下的价格

        for(int j = 1 ; j <= item[i].X ; j++)
        {
            scanf("%d%d" , &item[i].replace[j][0] , &item[i].replace[j][1]);
            G[item[i].replace[j][0]][i] = item[i].replace[j][1];
        }
    }
}

int dij(int l,int r)
{
    memset(vis,0,sizeof(vis));
    memset(low,0x1f,sizeof(low));
    vis[0]=1;
    low[0]=0;

    for(int i = 1 ; i <= n ; i++)
        low[i] = G[0][i];

    for(int i = 1 ; i <= n ; i++)
    {
        int minc=inf;
        int pos;
        for(int j = 0 ; j <= n ; j++)
        {
            if(item[j].lv >= l && item[j].lv <= r && !vis[j] && minc > low[j])
            {
                minc = low[j];
                pos = j;
            }
        }

        vis[pos]=1;
        if(pos == 1)
            break;

        for(int j = 0 ; j <= n ; j++)
        {
            if(item[j].lv >= l && item[j].lv <= r && !vis[j] && low[j] > G[pos][j] + low[pos])
                low[j] = G[pos][j] + low[pos];
        }
    }
    return low[1];
}

int main()
{
    read();
    int minc = inf;
    for(int i = item[1].lv-m >0 ? item[1].lv-m:0 ; i <= item[1].lv ; i++)
        minc = min(minc , dij(i,i+m));
    printf("%d\n",minc);
    return 0;
}


题目七:

ROADS

题意:

有K元钱,要从1-N,每条路都有一定长度,要付一定的钱,求在K元范围内到达N的最短距离


思路:

带有限制条件的最短路,具体注意点在代码里了,这里提几点:

1,使用优先队列,首先挑最短的,其次挑钱最少的,这很明显,所以挑出来的只要在K元内,只要能到N,必定就是当前路径长度最短的

2,注意是单向图...................坑了两个小时


代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int K,N,R;
int S,D,L,T;

struct edge
{
    int now;
    int next;
    int len;
    int value;
}e[10010];

struct node
{
    int id;
    int len;
    int coin;

    node(int x,int y,int c)
    {id = x , len = y , coin = c;}

    bool operator <(const node& a)const
    {
        if(len == a.len)
            return coin > a.coin;
        return len>a.len;
    }

};

int head[250] , vis[250];
int cnt=0;
int low[250];

void add(int x,int y,int len,int v)
{
    e[cnt].len = len;
    e[cnt].value = v;
    e[cnt].now = y;
    e[cnt].next = head[x];
    head[x] = cnt++;
}

int dij()
{
    low[1] = 0;
    priority_queue<node> que;
    que.push(node(1,0,0));

    while(!que.empty())
    {
        node cur = que.top();
        que.pop();
        low[cur.id] = cur.len;  //同一个id在队列里会有不同的状态,所以现在取出来的不一定就是最近更新的那一个状态
                                            //即状态回溯了
                                            //这句必不可少,而下面循环中的更新就可以不要了

        if(cur.id == N)             //到N了立刻退出,因为出队的方式是路径短的优先,如果不及时跳出,
            return low[N];          //后面可能会有同样到达N但路径必定长于当前的方案覆盖当前方案

        for(int i = head[cur.id] ; i != -1 ; i = e[i].next)
        {
            int now = e[i].now;
            node tmp(now , cur.len + e[i].len , cur.coin+ e[i].value);  //下一个待定点的参数
            if(cur.coin+ e[i].value <= K)
            {
                //low[now] = low[cur.id] + e[i].len ;     //如果符合条件,更改最短路径值
                que.push(tmp);
            }
        }
    }
    if(low[N] != inf)
        return low[N];
    return -1;
}

int main()
{
    scanf("%d%d%d" , &K,&N,&R);
    memset(head,-1,sizeof(head));
    memset(low,0x1f,sizeof(low));

    for(int i = 1 ; i <= R ; i++)
    {
        scanf("%d%d%d%d" , &S,&D,&L,&T);
        add(S,D,L,T);
    }

    printf("%d\n",dij());

    return 0;
}


题目八:

Currency Exchange

题意:

输入数据:

        N                               M                                                 S                                            V

货币种类数            货币间转换方法数               当前拥有哪一种货币                有多少这种货币

 A    B                      RAB                 CAB                       RBA                    CBA

货币A,货币B            A到B的汇率   A到B的手续费          B到A的汇率          B到A的手续费

...............


比如最初有20元A货币,A到B的转换汇率为1.10 , 手续费1.00 那么能得到B货币(20-1.00)*1.10元

最后问 能否经过一系列给定的转换使得最后换到A时钱数增加了


思路:

就是一个找环 , 找环的话本渣渣一般就想到两个:拓扑和最短路找负环

这里拓扑肯定不行,因为双向边会有冲突 , 那么就是最短路找负环了,而能处理负环的,掌握的最好的就是spfa了,好了,就是它了。

图的结点就是货币,权值就是汇率和手续费

其实就是把spfa模板改改,判定仍旧是一个点如果多次进入队列就表明有环,不过这里找的是正环

low[i]表示i货币当前能够拥有的最大数量


于是程序如下:

#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int N,M,S;
double V;
double G[110][110][2];
int vis[110];
double low[110];
int cnt[110];

int spfa()
{
    vis[S]=1;
    low[S] = V;
    queue<int> que;
    que.push(S);

    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur]=0;

        for(int i = 1 ; i <= N ; i++)
        {
            if(low[i] < (low[cur] -G[cur][i][1])*G[cur][i][0])
            {
                low[i] = (low[cur] -G[cur][i][1])*G[cur][i][0];

                if(!vis[i])
                {
                    vis[i]=1;
                    que.push(i);
                    cnt[i]++;
                }
            }
        }

        if(cnt[S] >= N)
            return 1;
    }

    if(low[S] > V)
        return 1;
    return 0;
}

int main()
{
    //freopen("Input.txt" , "r" , stdin);
    //freopen("out.txt","w",stdout);
    memset(vis,0,sizeof(vis));
    memset(low,0,sizeof(low));
    memset(G,0,sizeof(G));
    memset(cnt,0,sizeof(cnt));

    scanf("%d%d%d%lf" , &N,&M,&S,&V);

    for(int i = 0 ; i < M ; i++)
    {
        int a,b;
        double x,y,u,v;
        scanf("%d%d%lf%lf%lf%lf" , &a,&b,&x,&y,&u,&v);
        G[a][b][0] = x , G[a][b][1] = y;
        G[b][a][0] = u , G[b][a][1] = v;
    }

    cout << (spfa() ? "YES" : "NO") << endl;

    return 0;
}


题目九

Arbitrage


题意:

给你一群货币的名字和从A货币到B货币的汇率 , 判断是否有一种货币会在若干次汇率转换之后使自身的钱数增加


思路:

做过上一题后这题意图就很明显了,仍旧spfa找环,这里要注意的就是数据的读入和处理了,我是给每一种货币都编号,用map容器来映射编号


注意:

本题的所有建成的图都是连通图(AC后改了程序测试的,题面并没有说明),所以从第一个开始一次spfa就可以搞定了,但如果图不是连通的,那就要另外处理了,我这里反正这题数据小,就直接每个点爆枚了,数据量大,就得按照连通分量来减少处理次数。


代码:

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

map<string , int> lis;
double G[50][50];
int vis[50];
double low[50];
int cnt[50];
int n;

int spfa(int src)
{
    memset(vis , 0 , sizeof(vis));
    memset(low,0,sizeof(low));
    memset(cnt , 0 , sizeof(cnt));

    queue<int> que;
    que.push(src);
    vis[src]=1;
    low[src]=100;

    while(!que.empty())
    {
        int cur = que.front();
        vis[cur]=0;
        que.pop();

        for(int i = 1 ; i <= n ; i++)
        {
            if(low[i] < low[cur] * G[cur][i])
            {
                low[i] = low[cur] * G[cur][i];

                if(!vis[i])
                {
                    vis[i]=1;
                    que.push(i);
                    cnt[i]++;

                    if(cnt[i] > n)
                        return 1;
                }
            }
        }
    }

    return 0;
}


int main()
{
    //freopen("Input.txt" , "r" , stdin);
    //freopen("out.txt","w",stdout);

    int Case=1;

    while(cin >> n && n)
    {
        lis.clear();
        memset(G , 0 , sizeof(G));

        string line;

        for(int i = 1 ; i <= n  ;i++)
        {
            cin >> line;
            lis[line] = i;
        }

        int nn;
        cin >> nn;

        for(int i =1 ; i<= nn ; i++)
        {
            string l1,l2;
            double change;
            cin >> l1 >> change >>l2;
            G[lis[l1]][lis[l2]] = change;
        }

        printf("Case %d: ",Case++);
        int ok=0;
        for(int i = 1 ; i<=n ; i++)
        {
            ok = spfa(1);
            if(ok)
                break;
        }
        ok ? puts("Yes") : puts("No");
    }

    return 0;
}

题目十:

MPI Maelstrom

题意:给了一个图的邻接矩阵,x代表两点之间不可直接到达,求从1到其余每个点最短时间的最长时间

思路:裸最短路,只不过输入有点恶心


代码:

#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;

int n;
int G[110][110];
int low[110];
int vis[110];

int spfa()
{
    queue<int> que;
    que.push(1);
    vis[1] = 1;
    low[1] = 0;

    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur] = 0;

        for(int i = 1 ; i <= n ; i++)
        {
            if(low[i] > low[cur] + G[cur][i])
            {
                low[i] = low[cur] + G[cur][i];

                if(!vis[i])
                {
                    vis[i]=1;
                    que.push((i));
                }
            }
        }
    }

    return *max_element(low + 1 , low+n+1);
}

int main()
{
    //freopen("Input.txt" , "r" , stdin);
    //freopen("out.txt","w",stdout);

    while(~scanf("%d" , &n))
    {
        memset(vis,0,sizeof(vis));
        memset(low,0x1f,sizeof(low));
        memset(G,0x1f,sizeof(G));

        for(int i = 2 ; i <= n ; i++)
        {
            char Get[100];

            for(int j = 1 ; j < i ; j++)
            {
                scanf("%s" , Get);

                if(Get[0] == 'x')
                    continue;
                else
                    G[i][j] = G[j][i] = atoi(Get);
            }
        }

        printf("%d\n" , spfa());
    }

    return 0;
}

题目十一:

Simply Syntax

题意:

正确的句子的定义:

0.里面仅包含p~z ,N,C,D,E,I  (我觉得题面应该改一下,把这句里面的language改成sentence才不会误导别人)

1.每一个单个的p~z是一个正确句子

2.如果s是正句,那么Ns也是

3.如果s,t是正句,那么Cst,Dst,Est,Ist也是

4.0~3是唯一判断依据


现在给你一个句子,判断其是否是正确句子


思路:

从前向后遍历,不管如何,遇到N,是一句变一句,句子数量不增加,遇到其余四个大写字母,是两句变一句,句子数量减1,遇到小写字母,句子增加1

最后看看句子数是不是1就行了


YY的思路,还没有严密的证明。。。。。不过还真过了


代码如下:

#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;

int main()
{
    //freopen("Input.txt" , "r" , stdin);
    //freopen("out.txt","w",stdout);

    string line;
    while(cin >> line)
    {
        int juzi=0;

        for(int i = 0 ; i < line.size() ; i++)
        {
            if(line[i] >= 'p' && line[i] <= 'z')
                juzi++;
            else if(line[i] == 'C' || line[i] == 'D' || line[i] == 'E' || line[i] == 'I')
                juzi--;
            else if(line[i] == 'N')
                ;
            else
            {
                juzi = 0;
                break;
            }
        }

        cout << ((juzi == 1) ? "YES" : "NO" )<< endl;
    }

    return 0;
}


题目十二:

Subway

题意:

给起点和终点的两个坐标(输入第一行),接下来是一堆列车的站点,每一条线路都有至少两个站点,以坐标形式给出站点,(-1,-1) 结束一条线路站点的输入,

乘车是40km/h,走路10km/h 给的数据都是以m为单位的,最后求的是从起点到终点,最快几分钟能走完,注意是分钟


思路:

以每个站点和起终点为节点建图,边权:如果属于同一条线路则是SubwayTime,否则WalkTime,结构体中用id表示这个站点处于哪条线路


注意点:

1.单位转换

2.重边

3.一条线路,相邻两站才能以subwayTime可达,比如1->2->3 , 如果线路不是一条直线的话,1要到3则需经过2,才能用subwayTime计算

4.坑了我2小时的一个点,全怪我英语渣。。注意最后答案取的是最接近的一个整数,而不是向上或者向下取整


代码如下:

#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;

struct node
{
    int x;
    int y;
    int id;
}p[210];

int n = 2;
double G[210][210];
double low[210];
int vis[210];

double dis(node a,node b)
{
    return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + 1.0*(a.y-b.y)*(a.y-b.y));
}

double subwayTime(double dis)
{
    return dis/1000.0 / 40.0*60.0;
}

double walkTime(double dis)
{
    return dis/1000.0/10.0*60.0;
}

double spfa()
{
    queue<int> que;
    que.push(1);
    vis[1]=1;
    low[1]=0;

    while(!que.empty())
    {
        int cur = que.front();
        que.pop();
        vis[cur]=0;

        for(int i = 1 ; i <= n ; i++)
        {
            if(low[i] > low[cur] + G[cur][i])
            {
                low[i] = low[cur] + G[cur][i];

                if(!vis[i])
                {
                    vis[i]=1;
                    que.push(i);
                }
            }
        }
    }

    return low[2];
}

int main()
{
    //freopen("Input.txt" , "r" , stdin);
    //freopen("out.txt","w",stdout);

    scanf("%d%d%d%d" , &p[1].x , &p[1].y , &p[2].x , &p[2].y);
    memset(G , 127 , sizeof(G));
    memset(low,127,sizeof(low));

    int a,b;

    p[1].id=1 , p[2].id=2;
    int onaji=3;

    while(~scanf("%d%d" , &a,&b))
    {
        if(a == -1 && b == -1)
        {
            onaji++;
            continue;
        }

        n++;

        p[n].x = a , p[n].y = b , p[n].id = onaji;

        if(p[n-1].id == p[n].id)
            G[n-1][n] = G[n][n-1] = min(G[n-1][n] , subwayTime( dis(p[n-1],p[n]) ));

        for(int i = 1 ; i <= n ; i++)
            G[i][n] = G[n][i] = min(G[i][n] , walkTime( dis(p[i],p[n]) ));
    }

    cout << int(spfa()+0.5) << endl;

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值