解题报告:【kuangbin带你飞】专题四 最短路练习题

目录

    • A. POJ - 2387 T i l   t h e   C o w s   C o m e   H o m e Til\ the\ Cows\ Come\ Home Til the Cows Come Home--------(最短路模板题)【普及/提高-
    • B. POJ - 2253 F r o g g e r Frogger Frogger -------- (Floyd基础应用)【普及/提高-
    • C. POJ - 1797 H e a v y   T r a n s p o r t a t i o n Heavy\ Transportation Heavy Transportation -------- (最短路变形) 【普及/提高-
    • D. POJ 3268 S i l v e r   C o w   P a r t y Silver\ Cow\ Party Silver Cow Party -------- (单终点最短路+单源最短路)【普及+/提高
    • E. POJ - 1860 C u r r e n c y   E x c h a n g e Currency\ Exchange Currency Exchange -------- (判断正环/负环变形)【普及+/提高
    • F. POJ 3259 W o r m h o l e s Wormholes Wormholes --------(SPFA判断负环)【普及+/提高
    • G. POJ 1502 M P I   M a e l s t r o m MPI\ Maelstrom MPI Maelstrom-------- (矩阵处理/最短路模板)【普及/提高-
    • H. POJ 3660 C o w   C o n t e s t Cow\ Contest Cow Contest --------(Floyd传递闭包)【普及/提高-
    • I. POJ 2240 A r b i t r a g e Arbitrage Arbitrage -------- (判正环/Floyd)【普及+/提高
    • J. POJ 1511 I n v i t a t i o n   C a r d s Invitation\ Cards Invitation Cards提高+/省选-
    • K. POJ 3159 C a n d i e s Candies Candies
    • L. POJ 2502 S u b w a y Subway Subway
    • M. POJ 1062 昂贵的聘礼
    • N. POJ 1847 T r a m Tram Tram
    • O. LightOJ 1074 E x t e n d e d   T r a f f i c Extended\ Traffic Extended Traffic
    • P. HDU 4725 T h e   S h o r t e s t   P a t h   i n   N y a   G r a p h The\ Shortest\ Path\ in\ Nya\ Graph The Shortest Path in Nya Graph
    • Q. HDU 3416 M a r r i a g e   M a t c h I V Marriage\ Match IV Marriage MatchIV
    • R. HDU 4370 0   o r   1 0\ or\ 1 0 or 1
    • S. POJ 3169 L a y o u t Layout Layout --------(差分约束系统应用)【省选/NOI-

Kuangbin带你飞 专题四 最短路径 习题报告

A. POJ - 2387 T i l   t h e   C o w s   C o m e   H o m e Til\ the\ Cows\ Come\ Home Til the Cows Come Home--------(最短路模板题)【普及/提高-

Bessie is out in the field and wants to get back to the barn to get as
much sleep as possible before Farmer John wakes her for the morning
milking. Bessie needs her beauty sleep, so she wants to get back as
quickly as possible.

Farmer John’s field has N (2 <= N <= 1000) landmarks in it, uniquely
numbered 1…N. Landmark 1 is the barn; the apple tree grove in which
Bessie stands all day is landmark N. Cows travel in the field using T
(1 <= T <= 2000) bidirectional cow-trails of various lengths between
the landmarks. Bessie is not confident of her navigation ability, so
she always stays on a trail from its start to its end once she starts
it.

Given the trails between the landmarks, determine the minimum distance
Bessie must walk to get back to the barn. It is guaranteed that some
such route exists. Input

  • Line 1: Two integers: T and N

  • Lines 2…T+1: Each line describes a trail as three space-separated integers. The first two integers are the landmarks between which the
    trail travels. The third integer is the length of the trail, range
    1…100. Output

  • Line 1: A single integer, the minimum distance that Bessie must travel to get from landmark N to landmark 1.

最短路模板题

const int N = 50007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;

int head[N],edge[N << 1],nex[N << 1],ver[N << 1],tot;
int n,m,a[N];
int dis[N];

void add(int u,int v,int val){
     
    ver[++tot] = v;
    nex[tot] = head[u];
    edge[tot] = val;
    head[u] = tot;
}

void dijkstra(int s){
     
    //memset(vis,0,sizeof vis);
    for(int i = 1;i <= n;++i)
        dis[i] = INF;
    priority_queue<PII,vector<PII>,greater<PII> >q;
    dis[s] = 0;
    q.push({
     0,1});
    while(q.size()){
     
        int x = q.top().second;
        int d = q.top().first;
        q.pop();
        if(d != dis[x])continue;
        for(int i = head[x];i;i = nex[i]){
     
            int y = ver[i],z = edge[i];
            if(dis[y] > dis[x] + z){
     
                dis[y] = dis[x] + z;
                q.push({
     dis[y],y});
            }
        }
    }
}

int main(){
     
    while(~scanf("%d%d",&m,&n)){
     
        memset(head,0,sizeof head);
        for(int i = 1;i <= m;++i){
     
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
        }
        dijkstra(1);
        printf("%d\n",dis[n]);
    }
    return 0;
}

B. POJ - 2253 F r o g g e r Frogger Frogger -------- (Floyd基础应用)【普及/提高-

Freddy Frog is sitting on a stone in the middle of a lake. Suddenly he
notices Fiona Frog who is sitting on another stone. He plans to visit
her, but since the water is dirty and full of tourists’ sunscreen, he
wants to avoid swimming and instead reach her by jumping.
Unfortunately Fiona’s stone is out of his jump range. Therefore Freddy
considers to use other stones as intermediate stops and reach her by a
sequence of several small jumps. To execute a given sequence of jumps,
a frog’s jump range obviously must be at least as long as the longest
jump occuring in the sequence. The frog distance (humans also call it
minimax distance) between two stones therefore is defined as the
minimum necessary jump range over all possible paths between the two
stones.

You are given the coordinates of Freddy’s stone, Fiona’s stone and all
other stones in the lake. Your job is to compute the frog distance
between Freddy’s and Fiona’s stone. Input The input will contain one
or more test cases. The first line of each test case will contain the
number of stones n (2<=n<=200). The next n lines each contain two
integers xi,yi (0 <= xi,yi <= 1000) representing the coordinates of
stone #i. Stone #1 is Freddy’s stone, stone #2 is Fiona’s stone, the
other n-2 stones are unoccupied. There’s a blank line following each
test case. Input is terminated by a value of zero (0) for n. Output
For each test case, print a line saying “Scenario #x” and a line
saying “Frog Distance = y” where x is replaced by the test case number
(they are numbered from 1) and y is replaced by the appropriate real
number, printed to three decimals. Put a blank line after each test
case, even after the last one.
题目描述
一只叫 Freddy 的青蛙蹲坐在湖中的一块石头上,突然他发现一只叫 Fiona 的青蛙在湖中的另一块石头上。 Freddy 想要跟 Fiona 约会,但由于湖水太脏,他不想游泳过去而是跳过去找Fiona。很不幸,Fiona 所在的石头距离他有点远,甚至超出了他的跳跃能力。然而 Freddy 注意到湖中还有一些其他的石头。这些石头也许会将这个很长的跳跃距离化成若干个短的跳跃距离。我们定义“青蛙距离”为 Freddy 跳到 Fiona 那里所需要的若干次跳跃中最长的那一次。 现在给你 Freddy,Fiona,以及湖中其他石头的坐标,让你求出最短的“青蛙距离”。

首先是这里不是普通的最短路,需要求的最短路是最大单次跳跃距离最小化。需要用到动规的思想,加上数据只有n = 200,所以可以选择Floyd 。当然dijkstra也能过

const int N = 507;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;
/*
int head[N],edge[N << 1],nex[N << 1],ver[N << 1],tot;

void add(int u,int v,int val){
    ver[++tot] = v;
    nex[tot] = head[u];
    edge[tot] = val;
    head[u] = tot;
}
*/
int n,m;
int dis[N];
double G[N][N];
int t = 1;

struct node{
     
    int x,y;
}a[N];

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

int main(){
     
    while(~scanf("%d",&n) && n){
     
            memset(G,0x3f,sizeof G);
        for(int i = 1;i <= n;++i)
            scanf("%d%d",&a[i].x,&a[i].y);
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= n;++j)
                G[i][j] = distances(a[i].x,a[i].y,a[j].x,a[j].y);
        for(int k = 1;k <= n;++k)
            for(int i = 1;i <= n;++i)
                for(int j = 1;j <= n;++j)
                    G[i][j] = min(G[i][j],max(G[i][k],G[k][j]));
        cout<<"Scenario #"<<t++<<endl;
        cout<<"Frog Distance = ";
        printf("%.3f\n\n",G[1][2]);//保留三位小数
    }
}

C. POJ - 1797 H e a v y   T r a n s p o r t a t i o n Heavy\ Transportation Heavy Transportation -------- (最短路变形) 【普及/提高-

Problem You are given the plan of the city, described by the streets
(with weight limits) between the crossings, which are numbered from 1
to n. Your task is to find the maximum weight that can be transported
from crossing 1 (Hugo’s place) to crossing n (the customer’s place).
You may assume that there is at least one path. All streets can be
travelled in both directions. Input The first line contains the number
of scenarios (city plans). For each city the number n of street
crossings (1 <= n <= 1000) and number m of streets are given on the
first line. The following m lines contain triples of integers
specifying start and end crossing of the street and the maximum
allowed weight, which is positive and not larger than 1000000. There
will be at most one street between each pair of crossings. Output The
output for every scenario begins with a line containing “Scenario
#i:”, where i is the number of the scenario starting at 1. Then print a single line containing the maximum allowed weight that Hugo can
transport to the customer. Terminate the output for the scenario with
a blank line.

题目大意:n个点,m条边,求1到n的一条路径上的最小权值最大化。

要注意的是前向星对于每组数据初始化的时候,head数组和tot都要初始化。

以及这里要的是最小权值最大化,所以我们起点的dis数组先设为最大值INF,然后再往优先队列里扔(0,1)。

然后注意题目的输入描述,到底是输入n,m还是m,n

const int N = 500007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;

int head[N],edge[N << 1],nex[N << 1],ver[N << 1],tot;
int n,m,a[N];
int dis[N],cnt,vis[N];

void add(int u,int v,int val){
     
    ver[++tot] = v;
    nex[tot] = head[u];
    edge[tot] = val;
    head[u] = tot;
}

void dijkstra(int s){
     
    memset(vis,0,sizeof vis);
    for(int i = 1;i <= n;++i)
        dis[i] = 0;
    priority_queue<PII>q;
    dis[s] = INF;
    q.push({
     0,1});
    while(q.size()){
     
        int x = q.top().second;
        int d = q.top().first;
        q.pop();
        if(vis[x])continue;
        //if(d != dis[x])continue;
        vis[x] = 1;
        for(int i = head[x];i;i = nex[i]){
     
            int y = ver[i],z = edge[i];
            if(dis[y] < min(dis[x],z)){
     
                dis[y] = min(dis[x],z);
                q.push({
     dis[y],y});
            }
        }
    }
}

int main(){
     
    int T;
    scanf("%d",&T);
    while(T--){
     
        cnt++;
        scanf("%d%d",&n,&m);
        memset(head,0,sizeof head),tot = 1;
        for(int i = 1;i <= m;++i){
     
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
        }
        dijkstra(1);
        printf("Scenario #%d:\n%d\n\n", cnt, dis[n]);
    }
    return 0;
}

D. POJ 3268 S i l v e r   C o w   P a r t y Silver\ Cow\ Party Silver Cow Party -------- (单终点最短路+单源最短路)【普及+/提高

求 一个单源最短路 + 一个单终点最短路 的最大值。

思路:
我们首先想到的是从每个点都求一遍到终点的最短路,这样会加大时间复杂度。
所以,我们可以反向建图,直接把单终点最短路转为单源最短路,只需要跑两次最短路算法,显然是稳过的。(可以自己画画图,感受一下,单终点最短路选的路径与建反图之后的单源最短路选的路径一摸一样。当然本题直接当成任意两点间最短路用Floyd也能过,数据太弱了)

const int N = 5007;
const int M = 100007;
const int INF = 999999;
typedef pair<int,int> PII;

int head[N],edge[M],nex[M],ver[M],tot;
int n,m,a[N];
int dis[N],cnt,vis[N];
int ans[N],res;
int x[M],y[M],z[M];

void add(int u,int v,int val){
     
    ver[++tot] = v;
    nex[tot] = head[u];
    edge[tot] = val;
    head[u] = tot;
}

void dijkstra(int s){
     
    memset(vis,0,sizeof vis);
    for(int i = 1;i <= n;++i)
        dis[i] = INF;
    priority_queue<PII,vector<PII>,greater<PII> >q;
    dis[s] = 0;
    q.push({
     0,s});
    while(q.size()){
     
        int x = q.top().second;
        //int d = q.top().first;
        q.pop();
        if(vis[x])continue;
        //if(d != dis[x])continue;
        vis[x] = 1;
        for(int i = head[x];i;i = nex[i]){
     
            int y = ver[i],z = edge[i];
            if(dis[y] > dis[x] + z){
     
                dis[y] = dis[x] + z;
                q.push({
     dis[y],y});
            }
        }
    }
}

int s;
int main(){
     
    scanf("%d%d%d",&n,&m,&s);
    for(int i = 1;i <= m;++i){
     
        scanf("%d%d%d",&x[i],&y[i],&z[i]);
        add(x[i],y[i],z[i]);
    }
    dijkstra(s);
    //从终点回来是单源最短路
    //正图,求得回来的最短路
    for(int i = 1;i <= n;++i)
        ans[i] = dis[i];
    memset(dis,0,sizeof dis);
    memset(head,0,sizeof head);
    tot = 0;
    //建返图,将单终点最短路变成单源最短路
    for(int i = 1;i <= m;++i)
        add(y[i],x[i],z[i]);
    dijkstra(s);
    for(int i = 1;i <= n;++i)
        ans[i] += dis[i],res = max(res,ans[i]);
    cout<<res<<endl;
    return 0;
}

E. POJ - 1860 C u r r e n c y   E x c h a n g e Currency\ Exchange Currency Exchange -------- (判断正环/负环变形)【普及+/提高

题意: 输入货币种类的数量N(点的数量),货币兑换点(银行?)的数量M(边的数量),所持货币种类S(起点),初始货币数V
输入M行:两种货币的种类a、b、a->b的汇率、a->b的手续费、b->a的汇率、b->a的手续费。
判断最终能否通过一系列兑换使得所持货币总量增加。

思路:
SPFA判定正环,同负环类似,细节上做一些调整。
正环:一个环权值之和大于0- >这样走一定可以赚钱- >YES

注意若是走完的最长路是大于初始金钱V的话那么一样可以赚钱,所以输出YES

/*找最短路时更新++就是判负环*/
/*找最长路时更新++就是判正环*/
/*正环:一个环权值之和大于0->这样走可以赚钱*/
/*负环:一个环权值之和小于0*/
const int N = 2007;
const int M = 10007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;

int n,m;
int head[N],nex[M],ver[M],tot;
double exchange[M],cost[M];
double dis[N],V;
int vis[N],cnt[N],S;

void add(int u,int v,double ex,double co){
     
    ver[++tot] = v;
    exchange[tot] = ex;
    cost[tot] = co;
    nex[tot] = head[u];
    head[u] = tot;
}

bool spfa(){
     
    /*for(int i = 1;i <= n;++i)//最长路,置负值
        dis[i] = -INF;*/
    memset(dis,-INF,sizeof dis);
    dis[S] = V;
    cnt[S]++;
    queue<int>q;
    q.push(S);
    while(q.size()){
     
        int x = q.front();q.pop();
        vis[x] = 0;
        for(int i = head[x];i;i = nex[i]){
     
            int y = ver[i];
            double ex = exchange[i],co = cost[i];
            double val = (dis[x] - co) * ex;
            if(dis[y] < val){
     
                dis[y] = val;
                cnt[y] = cnt[x] + 1;
                if(cnt[y] > n)return true;
                if(!vis[y]){
     
                    vis[y] = 1;
                    q.push(y);
                }
            }
        }
    }//尽管没有正环但是经过一次最长路以后还是比原来的大说明可以赚
    if(dis[S] > V)return true;
    return false;
}

int main(){
     
    scanf("%d%d%d%lf",&n,&m,&S,&V);
    for(int i = 1;i <= m;++i){
     
        int x,y;
        double ex,co;
        scanf("%d%d%lf%lf",&x,&y,&ex,&co);
        add(x,y,ex,co);
        scanf("%lf%lf",&ex,&co);
        add(y,x,ex,co);
    }
    if(spfa())puts("YES");
    else puts("NO");
    return 0;
}


F. POJ 3259 W o r m h o l e s Wormholes Wormholes --------(SPFA判断负环)【普及+/提高

农夫约翰在探索他的许多农场,发现了一些惊人的虫洞。虫洞是很奇特的,因为它是一个单向通道,可让你进入虫洞的前达到目的地!他的N(1≤N≤500)个农场被编号为1…N,之间有M(1≤M≤2500)条路径,W(1≤W≤200)个虫洞。FJ作为一个狂热的时间旅行的爱好者,他要做到以下几点:开始在一个区域,通过一些路径和虫洞旅行,他要回到最开时出发的那个区域出发前的时间。也许他就能遇到自己了:)。为了帮助FJ找出这是否是可以或不可以,他会为你提供F个农场的完整的映射到(1≤F≤5)。所有的路径所花时间都不大于10000秒,所有的虫洞都不大于万秒的时间回溯。
Input 第1行:一个整数F表示接下来会有F个农场说明。 每个农场第一行:分别是三个空格隔开的整数:N,M和W
第2行到M+1行:三个空格分开的数字(S,E,T)描述,分别为:需要T秒走过S和E之间的双向路径。两个区域可能由一个以上的路径来连接。 第M
+2到M+ W+1行:三个空格分开的数字(S,E,T)描述虫洞,描述单向路径,S到E且回溯T秒。 Output F行,每行代表一个农场 每个农场单独的一行,” YES”表示能满足要求,”NO”表示不能满足要求。 如果两点之间存在负环,那么他们之间不可能存在最短路!!!

注意看题,虫洞是单向的,但是普通的路是双向的。
要仔细看清题!!
没有起点,那么每个人都是起点。

const int N = 2007;
const int M = 10007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;
int ver[M],nex[M],edge[M],head[N],tot;
int cnt[N],vis[N],dis[N];
int n,m,w;

void add(int x,int y,int z){
     
    ver[++tot] = y;
    edge[tot] = z;
    nex[tot] = head[x];
    head[x] = tot;
}

bool spfa(){
     
    queue<int>q;
    for(int i = 1;i <= n;++i){
     
        q.push(i);
        dis[i] = 0;
        vis[i] = 1;
    }
    memset(cnt,0,sizeof cnt);
    while(q.size()){
     
        int x = q.front();
        q.pop();
        vis[x] = 0;
        for(int i = head[x];i;i = nex[i]){
     
            int y = ver[i],z = edge[i];
            if(dis[y] > dis[x] + z){
     
                dis[y] = dis[x] + z;
                cnt[y] = cnt[x] + 1;
                if(cnt[y] >= n)return true;
                if(!vis[y]){
     
                    vis[y] = 1;
                    q.push(y);
                }
            }
        }
    }
    return false;
}

int T;
int main(){
     
    cin>>T;
    while(T--){
     
        memset(head,0,sizeof head),tot = 0;
        scanf("%d%d%d",&n,&m,&w);
        for(int i = 1; i<= m;++i){
     
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);add(y,x,z);
        }
        for(int i = 1; i<= w;++i){
     
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,-z);
        }
        if(spfa())puts("YES");
        else puts("NO");
    }
    return 0;
}

G. POJ 1502 M P I   M a e l s t r o m MPI\ Maelstrom MPI Maelstrom-------- (矩阵处理/最短路模板)【普及/提高-

实验室有很多台计算机,由于每个人计算机的性能不同,导致计算机之间发送信息的速度不同,所以花费时间不同。
消息从第一台电脑发送到第二台电脑后,这两台电脑能再向其他电脑发送消息,就是一种类似二叉树的结构。
当然并不是真正的二叉树——我们的计算机有一些特殊的特性,我们应该加以利用。 我们的计算机允许同时向连接到它的任意数量的其他计算机发送消息。
然而,消息不一定同时到达目的地——这涉及到计算机配置。
一般来说,我们需要考虑到拓扑结构中每个计算机的时间成本,并相应地计划将消息传播所需的总时间降到最低。
涂爷需要经常给其他人发消息,她想要知道如果她发出一个消息,至少要等多久全部的人才可以都收到通知。 Input
第一行输入一个n,表示有多少台计算机(涂爷当然是一号机啦~)。 随后n-1行,以邻接矩阵的形式给出计算机(1~n)之间通讯需要的时间。
由于两台计算机相互通信时间相等,且计算机自己和自己不需要通信,所以只给出了矩阵的下三角。
ps:x表示由于部分计算机之间存在特殊的磁场并不能通信。 ps2:该题所有数据范围0~100。 Output
输出一行表示涂爷需要等待的时间。.

最短路模板…
注意输入的时候是一个矩阵,以及字符串输入用atoi将char转换成int

const int N = 2007;
const int M = 10007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;
int n,m;
char s[100];
int dis[N][N];


int main(){
     
    cin>>n;
    for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
            if(i == j)dis[i][j] = 0;
            else dis[i][j]  = dis[j][i] =  INF;
    for(int i = 2;i <= n;++i)
        for(int j = 1;j <= i - 1;++j){
     
            scanf("%s",s);
            if(s[0] == 'x')continue;
            int val = atoi(s);
            dis[i][j] = dis[j][i] = val;
        }
    for(int k = 1;k <= n;++k)
        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]);
    int ans = 0;
    for(int i = 1;i <= n ;++i)
        ans = max(ans,dis[1][i]);
    cout<<ans<<endl;
    return 0;
}

H. POJ 3660 C o w   C o n t e s t Cow\ Contest Cow Contest --------(Floyd传递闭包)【普及/提高-

题目描述
N (1 ≤ N ≤ 100) cows, conveniently numbered 1…N, are
participating in a programming contest. As we all know, some cows code
better than others. Each cow has a certain constant skill rating that
is unique among the competitors. The contest is conducted in several
head-to-head rounds, each between two cows. If cow A has a greater
skill level than cow B (1 ≤ A ≤ N; 1 ≤ B ≤ N; A ≠ B), then cow A will
always beat cow B. Farmer John is trying to rank the cows by skill
level. Given a list the results of M (1 ≤ M ≤ 4,500) two-cow rounds,
determine the number of cows whose ranks can be precisely determined
from the results. It is guaranteed that the results of the rounds will
not be contradictory.

FJ的N(1 <= N <= 100)头奶牛们最近参加了场程序设计竞赛:)。在赛场上,奶牛们按1…N依次编号。每头奶牛的编程能力不尽相同,并且没有哪两头奶牛的水平不相上下,也就是说,奶牛们的编程能力有明确的排名。
整个比赛被分成了若干轮,每一轮是两头指定编号的奶牛的对决。如果编号为A的奶牛的编程能力强于编号为B的奶牛(1 <= A <= N; 1 <=
B <= N; A != B) ,那么她们的对决中,编号为A的奶牛总是能胜出。 FJ想知道奶牛们编程能力的具体排名,于是他找来了奶牛们所有
M(1 <= M <= 4,500)轮比赛的结果,希望你能根据这些信息,推断出尽可能多的奶牛的编程能力排名。比赛结果保证不会自相矛盾。
输入格式 第1行: 2个用空格隔开的整数:N 和 M 第2…M+1行: 每行为2个用空格隔开的整数A、B,描述了参加某一轮比赛的奶
牛的编号,以及结果(编号为A,即为每行的第一个数的奶牛为 胜者) 输出格式 第1行: 输出1个整数,表示排名可以确定的奶牛的数目

这个说明就讲的很清楚
说明/提示
输出说明:
编号为2的奶牛输给了编号为1、3、4的奶牛,也就是说她的水平比这3头奶
牛都差。而编号为5的奶牛又输在了她的手下,也就是说,她的水平比编号为5的
奶牛强一些。于是,编号为2的奶牛的排名必然为第4,编号为5的奶牛的水平必
然最差。其他3头奶牛的排名仍无法确定。

所以我们直接用Floyd传递闭包

根据题意既是如果i能赢k,k能赢j,那么i能赢j。说明关系可传递,使用Floyd传递闭包。f[i][j] = 1表示i能胜j。(如果i能赢k,j也能赢k,那么i和j的胜负不定,就没办法确定最终排名了。)因此如果一头牛能与剩下n-1头都能确定胜负关系,那么就能确定它的排名。
我们用f[x][y]表示x可以赢y,这样只有i能赢k,k能赢j,才能确定i能赢j,也就是f[i][j] = 1

然后就是传递闭包的模板了 优美的位运算

const int N = 2007;
const int M = 10007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;

int n,m;
int f[N][N];
int ans;

int main(){
     
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;++i){
     
        int x,y;
        scanf("%d%d",&x,&y);
        f[x][y] = 1;
    }
    for(int k = 1;k <= n;++k)
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= n;++j)
                f[i][j] |= f[i][k] & f[k][j];
    for(int i = 1;i <= n;++i){
     
        int now = 1;
        for(int j = 1;j <= n;++j)
            if(i != j)
                now &= f[i][j] | f[j][i];
        ans += now;
    }
    cout<<ans<<endl;
    return 0;
}

I. POJ 2240 A r b i t r a g e Arbitrage Arbitrage -------- (判正环/Floyd)【普及+/提高

Input
套利是指利用货币汇率的差异,将一种货币的一个单位转换为同一货币的多个单位。例如,假设1美元买0.5英镑,1英镑买10.0法国法郎,1法国法郎买0.21美元。然后,通过兑换货币,聪明的交易者可以从1美元开始,购买0.5 * 10.0 * 0.21 = 1.05美元,获得5%的利润。 您的工作是编写一个程序,以货币汇率列表作为输入,然后确定是否可能进行套利。

Output
输入将包含一个或多个测试用例。每个测试用例的第一行有一个整数n(1<=n<=30),表示不同货币的数量。接下来的n行每一行都包含一种货币的名称。名称中不会出现空格。下一行包含一个整数m,表示接下来的表的长度。最后的m行分别包含源货币的名称ci、表示从ci到cj的汇率的实数rij和目标货币的名称cj。没有出现在表格中的交易是不可能的。
测试用例通过空行彼此分开。对于n,输入以0(0)的值结束。

直接spfa判正环即可。因为数据范围比较小,我们也可以直接用Floyd求一个最长路,然后看是否有一种货币能升值即可。

注意本题数据又double,要分清各个变量的类型,不然会WA

const int N = 100;
const int M = 10007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;

int n,m;
double f[N][N];
char name1[100],name2[100];

bool Floyd(){
     
    for(int k = 1;k <= n;++k)
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= n;++j)
                f[i][j] = max(f[i][j],f[i][k] * f[k][j]);
    for(int i = 1;i <= n;++i)
        if(f[i][i] > 1)return true;
    return false;
}

int main(){
     
    int T = 0;
    while(cin>>n && n){
     
        memset(f,0,sizeof f);
        map<string,int>mp;
        for(int i = 1;i <= n;++i){
     
            scanf("%s",name1);
            mp[name1] = i;
        }
        cin>>m;
        for(int i = 1;i <= m;++i){
     
            double val;
            scanf("%s%lf%s",name1,&val,name2);
            f[mp[name1]][mp[name2]] = val;
        }
        printf("Case %d: %s\n",++T,Floyd()?"Yes":"No");
    }
    return 0;
}

J. POJ 1511 I n v i t a t i o n   C a r d s Invitation\ Cards Invitation Cards提高+/省选-

Description

n-1个人从1号点出发,到剩余n-1个宣传点,然后再回到1号点汇报结果,求所有人往返路径和的最小值

Input

输入由T个案例组成。输入的第一行只包含正整数T。 接下来是N和M,1 <= N,M <= 1000000,表示N个点和连接N个点的M条边。 然后有M行,每行包括三个值U,V,W,表示从点U到点V需要W的路程。你可以假设该图连通。
注意是单向通道!!!

Output 对于每个案例,打印一行,表示路径总和的最小值。

#include
#include
#include
#include
typedef long long ll;
using namespace std;
const int N = 1000007, M = 5000007;
const ll INF = 0x3f3f3f3f3f;

int n, m;
ll dist[M], rdist[M];
int w[N];
int head[N], rhead[N], edge[M], nex[M], ver[M], tot;
int q[M];
bool vis[M];

void add(int *h, int x, int y, int z){
     
    ver[tot] = y;
    edge[tot] = z;
    nex[tot] = h[x];
    h[x] = tot ++ ;
}

void spfa(int *h, ll *d, int s){
     
    int hh = 0, tt = -1;
    memset(d, 0x3f, sizeof dist);

    q[++ tt] = s;
    if(tt == M)tt = 0;
    vis[s] = true;
    d[s] = 0;

    while(hh <= tt){
     
        int x = q[hh ++ ];
        if(hh == M)hh = 0;
        vis[x] = false;

        for(int i = h[x]; ~i; i = nex[i]){
     
            int y = ver[i], z = edge[i];
            if(d[y] > d[x] + z){
     
                d[y] = d[x] + z;
                if(!vis[y]){
     
                    q[++ tt] = y;
                    if(tt == M)tt = 0;
                    vis[y] = true;
                }
            }
        }
    }
}

int main()
{
     
    int t;
    scanf("%d", &t);
    while(t -- ){
     
        memset(head, -1, sizeof head);
        memset(rhead, -1, sizeof rhead);
        tot = 0;

        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; ++ i){
     
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add(head, a, b, c);
            add(rhead,b, a, c);
        }
        ll res = 0;

        spfa(head, dist, 1);
        spfa(rhead, rdist, 1);

        for(int i = 1; i <= n; ++ i)
            res += dist[i] + rdist[i];
        printf("%lld\n", res);
    }
    return 0;
}

K. POJ 3159 C a n d i e s Candies Candies

L. POJ 2502 S u b w a y Subway Subway

M. POJ 1062 昂贵的聘礼

N. POJ 1847 T r a m Tram Tram

O. LightOJ 1074 E x t e n d e d   T r a f f i c Extended\ Traffic Extended Traffic

P. HDU 4725 T h e   S h o r t e s t   P a t h   i n   N y a   G r a p h The\ Shortest\ Path\ in\ Nya\ Graph The Shortest Path in Nya Graph

Q. HDU 3416 M a r r i a g e   M a t c h I V Marriage\ Match IV Marriage MatchIV

R. HDU 4370 0   o r   1 0\ or\ 1 0 or 1

S. POJ 3169 L a y o u t Layout Layout --------(差分约束系统应用)【省选/NOI-

这里是排过序的,原来的模板是 x i − x j ≤ k x_i - x_j ≤ k xixjk,从ji,现在因为从小到大排序了,所以反过来了,是 x j − x i ≤ k x_j - x_i ≤ k xjxik − > - > > x j ≤ x i + c k x_j \leq x_i+ c_k xjxi+ck所以应该从ij连权值为k的边。
然后就是模板了。
根据题目要求先从0出发判断有无负环,若有负环输出-1
然后从1出发求一次,若dis[n]==INF则输出-2
否则差分约束系统通过最短路求出的dis数组就是一组满足差分约束的解,1n的最大差即为dis[n]

const int N = 5007;
const int M = 50007;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;

int n,m;
int ver[M],edge[M],nex[M],head[N],tot;
int dis[N],vis[N],cnt[N];

void add(int x,int y,int z){
     
    ver[++tot] = y;
    edge[tot] = z;
    nex[tot] = head[x];
    head[x] = tot;
}

bool spfa(int s){
     
    queue<int>q;
    memset(vis,0,sizeof vis);
    memset(dis,0x3f,sizeof dis);
    memset(cnt,0,sizeof cnt);
    dis[s] = 0;vis[s] = 1;
    q.push(s);
    while(q.size()){
     
        int x = q.front();
        q.pop();
        vis[x] = 0;
        for(int i = head[x];i;i = nex[i]){
     
            int y = ver[i],z = edge[i];
            if(dis[y] > dis[x] + z){
     
                dis[y] = dis[x] + z;
                cnt[y] = cnt[x] + 1;
                if(cnt[y] >= n)return false;
                if(!vis[y]){
     
                    vis[y] = 1;
                    q.push(y);
                }
            }
        }
    }
    return true;
}
int o;
int main(){
     
    scanf("%d%d%d",&n,&m,&o);
    for(int i = 1;i <= n;++i)//建超级源点
        add(0,i,0);
    for(int i = 1;i <= m;++i){
     
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);//xi - xj ≤ k//j来更新i,所以j向i连一条权值为k的边
    }
    for(int i = 1;i <= o;++i){
     
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(y,x,-z);
    }
    if(!spfa(0))puts("-1");
    else {
     
        spfa(1);
        if(dis[n] == INF)puts("-2");
        else cout<<dis[n]<<endl;
    }
    return 0;
}

你可能感兴趣的:(kuangbin专题合集,#,最短路算法,最短路,图论)