数据结构专题系列 基础篇-9-2 图论-最短路
--------------------------------------------------------------------------------
题目传送门
洛谷图论-最短路 普及- 组
洛谷图论-最短路 普及/提高- 组
--------------------------------------------------------------------------------
更多详见>>
--------------------------------------------------------------------------------
序
这里是数据结构专题系列 基础篇-9-2 图论-最短路
图论是一个大家族,本章我们讨论图论的其中一个分支——最短路问题,这个问题不仅最基础,而且算法也最广泛,包括了Dijkstra、Floyd、Bellman-Ford、SPFA等,基础部分着重讨论这4种算法,提高部分会有相应的算法扩展和补充
更多内容详见:OI WIKI
其中,
Dijkstra算法的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
Floyd算法的时间复杂度为 O ( n 3 ) O(n^3) O(n3)
Bellman-Ford算法的时间复杂度为 O ( n E ) O(nE) O(nE)
SPFA算法的时间复杂度为 O ( k E ) O(kE) O(kE),注意此处的k是不可忽略的常数,常数波动范围很大,从 2 − n 2-n 2−n都有可能,因此,SPFA在某些特殊的图中会退化成Bellman-Ford算法!
本章一共1+25题,推荐先花若干时间学明白最短路思想,然后分2-4周完成习题,这样有助于巩固所学知识!
接下来就是题解部分了,每道算法题都标注有对应的算法标签,对于那些易错、较难或是测试点比较特殊的题目会着重标注,本章推荐的题目有:
P5651 基础最短路练习题 | 图论 + Dijkstra + Kruskal + DFS |
---|---|
P6833 [Cnoi2020]雷雨 | 图论 + Dijkstra + 枚举 |
其余题目 | 模板题 |
--------------------------------------------------------------------------------
难度: 普及-
P2910 [USACO08OPEN]Clear And Present Danger S
算法标签: 图论 + Floyd
注意点: 全源最短路径应优先考虑Floyd算法
#include<bits/stdc++.h>
using namespace std;
int N,M;
int a[10005];
int dis[105][105];
int main(){
scanf("%d%d",&N,&M);
memset(dis,0x3f,sizeof(dis));
for(int i=0;i<M;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
scanf("%d",&dis[i][j]);
}
}
for(int k=1;k<=N;k++){
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
if((i!=j) && (j!=k) && (k!=i)){
dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
}
int ans = 0;
for(int i=0;i<M-1;i++){
ans += dis[a[i]][a[i+1]];
}
printf("%d\n",ans);
return 0;
}
难度: 普及/提高-
P2935 [USACO09JAN]Best Spot S
算法标签: 图论 + Dijkstra + 枚举
注意点: 依次枚举每个顶点作为Bessie的出发点,做N轮Dijkstra,求最短路径和, ∑ 最 短 路 径 \sum 最短路径 ∑最短路径最小的那个点即为Bessie的出发点
#include<bits/stdc++.h>
using namespace std;
int P,F,C;
int a[505];
bool vis[505];
struct node{
int v;
int dis;
};
node temp;
vector<node> G[505];
int dp[505];
void dijkstra(int u){
priority_queue<pair<int,int> > q;
dp[u] = 0;
q.push(make_pair(0,u));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int u = f.second;
if(vis[u]){
continue;
}
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].dis;
if(!vis[v] && dp[u]+w<dp[v]){
dp[v] = dp[u] + w;
q.push(make_pair(-dp[v],v));
}
}
}
}
int main(){
scanf("%d%d%d",&P,&F,&C);
for(int i=1;i<=F;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=C;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
temp.v = v;
temp.dis = w;
G[u].push_back(temp);
temp.v = u;
G[v].push_back(temp);
}
int res = INT_MAX;
int site = -1;
for(int i=1;i<=P;i++){
memset(dp,0x3f,sizeof(dp));
memset(vis,0,sizeof(vis));
dijkstra(i);
int ans = 0;
for(int j=1;j<=F;j++){
ans += dp[a[j]];
}
if(ans < res){
res = ans;
site = i;
}
}
cout << site << endl;
return 0;
}
P2984 [USACO10FEB]Chocolate Giving S
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题
#include<bits/stdc++.h>
using namespace std;
int N,M,B;
struct Edge{
int value;
int to;
int next;
};
Edge e[200005];
int head[50010];
int cnt = 0;
int dis[50010];
bool vis[50010];
int s,t;
void addedge(int a,int b,int v){
cnt++;
e[cnt].value = v;
e[cnt].to = b;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
void SPFA(){
int u;
queue<int> q;
dis[1] = 0;
vis[1] = true;
q.push(1);
while(!q.empty()){
u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u];i;i=e[i].next){
if(dis[u] + e[i].value < dis[e[i].to]){
dis[e[i].to] = dis[u] + e[i].value;
if(!vis[e[i].to]){
vis[e[i].to] = true;
q.push(e[i].to);
}
}
}
}
}
int main(){
scanf("%d%d%d",&N,&M,&B);
for(int i=1;i<=M;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
memset(dis,0x3f,sizeof(dis));
SPFA();
for(int i=1;i<=B;i++){
int s,t;
scanf("%d%d",&s,&t);
printf("%d\n",dis[s] + dis[t]);
}
return 0;
}
P1339 [USACO09OCT]Heat Wave G
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t1;
int head[25050];
int cnt = 0;
int dis[25050];
struct Edge{
int to;
int value;
int next;
};
Edge edge[62050];
struct vertex{
int v;
int dis;
vertex(){
}
vertex(int v1,int dis1){
v = v1;
dis = dis1;
}
bool operator < (const vertex &x)const{
return x.dis < dis;
}
};
priority_queue<vertex> q;
void addedge(int a,int b,int v){
cnt++;
edge[cnt].to = b;
edge[cnt].value = v;
edge[cnt].next = head[a];
head[a] = cnt;
return;
}
int main(){
scanf("%d%d%d%d",&n,&m,&s,&t1);
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
memset(dis,0x3f,sizeof(dis));
dis[s] = 0;
q.push(vertex(s,0));
vertex t;
while(!q.empty()){
t = q.top();
q.pop();
for(int i=head[t.v];i!=0;i=edge[i].next){
if(dis[t.v] + edge[i].value < dis[edge[i].to]){
dis[edge[i].to] = dis[t.v] + edge[i].value;
q.push(vertex(edge[i].to,dis[edge[i].to]));
}
}
}
printf("%d\n",dis[t1]);
return 0;
}
P5651 基础最短路练习题
算法标签: 图论 + Dijkstra + Kruskal + DFS
注意点: 一道好题!根据异或的性质不难发现, d i s ( x , y ) = a x x o r a y dis(x,y)=a_x\ xor \ a_y dis(x,y)=ax xor ay,因此,只需要DFS递推一遍所有的点权即可,不妨假设点1的点权为0,递推得到最终结果,再查询输出
那么这题为何用到最小生成树算法呢?因为题目中说对于所有的简单环,异或和都为0,这就意味着从点x到点y的所有简单路径长度都一致,也意味着只需保留一棵树,等价于计算了所有的顶点的最短距离,减少了时间复杂度(枚举边)
#include<bits/stdc++.h>
using namespace std;
int n,m,Q;
const int maxn = 1e5+5;
int father[maxn];
int dp[maxn];
bool vis[maxn];
struct node{
int x,y;
int dis;
};
struct edge{
int v;
int dis;
};
edge temp;
node t[2*maxn];
vector<edge> G[maxn];
int cnt = 0;
int findfather(int x){
if(x == father[x]){
return x;
}
int F = findfather(father[x]);
father[x] = F;
return F;
}
void Kruskal(){
int ans = 0;
for(int i=1;i<=n;i++){
father[i] = i;
}
for(int i=0;i<cnt;i++){
int fa = findfather(t[i].x);
int fb = findfather(t[i].y);
if(fa!=fb){
father[fa] = fb;
temp.v = t[i].y;
temp.dis = t[i].dis;
G[t[i].x].push_back(temp);
temp.v = t[i].x;
G[t[i].y].push_back(temp);
ans++;
}
if(ans == n-1){
break;
}
}
}
void dfs(int u){
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].dis;
if(!vis[v]){
dp[v] = dp[u] ^ w;
dfs(v);
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&t[cnt].x,&t[cnt].y,&t[cnt].dis);
cnt++;
}
Kruskal();
dp[1] = 0;
dfs(1);
for(int i=1;i<=Q;i++){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",dp[x]^dp[y]);
}
return 0;
}
P1576 最小花费
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct Edge{
double value;
int to;
int next;
};
Edge e[400010];
int head[2005];
bool vis[2005];
double dis[2005];
int cnt = 0;
int A,B;
void addedge(int a,int b,double v){
cnt++;
e[cnt].to = b;
e[cnt].value = v;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
void SPFA(){
int u;
queue<int> q;
q.push(A);
dis[A] = 1.0;
vis[A] = true;
while(!q.empty()){
u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u];i!=0;i=e[i].next){
if(dis[u]*e[i].value>dis[e[i].to]){
dis[e[i].to] = dis[u] * e[i].value;
if(!vis[e[i].to]){
q.push(e[i].to);
vis[e[i].to] = true;
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
fill(dis+1,dis+n+1,0);
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
double d = (double)(100-w) * 1.0 / 100.0;
addedge(u,v,d);
addedge(v,u,d);
}
scanf("%d%d",&A,&B);
SPFA();
printf("%.8lf\n",100.0/dis[B]);
return 0;
}
P1821 [USACO07FEB] Cow Party S
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m,x;
struct node{
int v;
int dis;
};
node temp;
vector<node> G1[1005];
vector<node> G2[1005];
int dp1[1005];
int dp2[1005];
bool vis[1005];
int ans = 0;
void dijkstra(int u,vector<node> G[1005],int dp[1005]){
memset(vis,0,sizeof(vis));
priority_queue<pair<int,int> > q;
dp[u] = 0;
q.push(make_pair(-dp[u],u));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int u = f.second;
if(vis[u]){
continue;
}
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].dis;
if(!vis[v] && dp[u] + w < dp[v]){
dp[v] = dp[u] + w;
q.push(make_pair(-dp[v],v));
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&x);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
temp.v = v;
temp.dis = w;
G1[u].push_back(temp);
temp.v = u;
G2[v].push_back(temp);
}
memset(dp1,0x3f,sizeof(dp1));
memset(dp2,0x3f,sizeof(dp2));
dijkstra(x,G1,dp1);
dijkstra(x,G2,dp2);
for(int i=1;i<=n;i++){
ans = max(ans,dp1[i] + dp2[i]);
}
cout << ans << endl;
return 0;
}
P1744 采购特价商品
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m;
int S,T;
const double INF = 1e20;
struct site{
int x,y;
};
site s[105];
double dis[105];
int head[105];
bool vis[105];
struct Edge{
int to;
int next;
double value;
};
Edge e[2005];
int cnt = 0;
void addedge(int a,int b,double v){
cnt++;
e[cnt].value = v;
e[cnt].to = b;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
double distance1(site s1,site s2){
double res;
res = sqrt((s1.x-s2.x)*(s1.x-s2.x)+(s1.y-s2.y)*(s1.y-s2.y));
return res;
}
void SPFA(){
int u;
dis[S] = 0.0;
vis[S] = true;
queue<int> q;
q.push(S);
while(!q.empty()){
u = q.front();
vis[u] = false;
q.pop();
for(int i=head[u];i!=0;i=e[i].next){
if(dis[u] + e[i].value < dis[e[i].to]){
dis[e[i].to] = dis[u] + e[i].value;
if(!vis[e[i].to]){
q.push(e[i].to);
vis[e[i].to] = true;
}
}
}
}
}
int main(){
scanf("%d",&n);
fill(dis+1,dis+n+1,INF);
for(int i=1;i<=n;i++){
scanf("%d%d",&s[i].x,&s[i].y);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
double dis = distance1(s[u],s[v]);
addedge(u,v,dis);
addedge(v,u,dis);
}
scanf("%d%d",&S,&T);
SPFA();
printf("%.2lf",dis[T]);
return 0;
}
P2299 Mzc和体委的争夺战
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct node{
int v;
int dis;
};
node temp;
vector<node> G[2505];
int dp[2505];
bool vis[2505];
void dijstra(int u){
memset(dp,0x3f,sizeof(dp));
memset(vis,0,sizeof(vis));
dp[u] = 0;
priority_queue<pair<int,int> > q;
q.push(make_pair(-dp[u],u));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int u = f.second;
if(vis[u]){
continue;
}
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].dis;
if(!vis[v] && dp[u]+w<dp[v]){
dp[v] = dp[u] + w;
q.push(make_pair(-dp[v],v));
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
temp.v = v;
temp.dis = w;
G[u].push_back(temp);
temp.v = u;
G[v].push_back(temp);
}
dijstra(1);
cout << dp[n] << endl;
return 0;
}
P1364 医院设置
算法标签: 图论 + Dijkstra + 枚举
注意点: 枚举每个点,假设其为最可能设置的点,统计带权路径长度,取最小值后输出
#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
int v;
int dis;
};
node temp;
vector<node> G[105];
int dp[105];
bool vis[105];
int a[105];
int res = INT_MAX;
void dijstra(int u){
memset(dp,0x3f,sizeof(dp));
memset(vis,0,sizeof(vis));
dp[u] = 0;
priority_queue<pair<int,int> > q;
q.push(make_pair(-dp[u],u));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int u = f.second;
if(vis[u]){
continue;
}
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].dis;
if(!vis[v] && dp[u]+w<dp[v]){
dp[v] = dp[u] + w;
q.push(make_pair(-dp[v],v));
}
}
}
int ans = 0;
for(int i=1;i<=n;i++){
ans += a[i] * dp[i];
}
res = min(res,ans);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int w,l,r;
scanf("%d%d%d",&w,&l,&r);
temp.dis = 1;
a[i] = w;
if(l){
temp.v = l;
G[i].push_back(temp);
temp.v = i;
G[l].push_back(temp);
}
if(r){
temp.v = r;
G[i].push_back(temp);
temp.v = i;
G[r].push_back(temp);
}
}
for(int i=1;i<=n;i++){
dijstra(i);
}
cout << res << endl;
return 0;
}
P1529 [USACO2.4]回家 Bessie Come Home
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int P;
int head[1005];
int cnt = 0;
int dis[1005];
struct Edge{
int to;
int value;
int next;
};
Edge e[100100];
struct vertex{
int v;
int dis;
vertex(){
}
vertex(int v1,int dis1){
v = v1;
dis = dis1;
}
bool operator < (const vertex &x)const{
return x.dis < dis;
}
};
priority_queue<vertex> q;
void addedge(int a,int b,int v){
cnt++;
e[cnt].to = b;
e[cnt].value = v;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
int main(){
scanf("%d",&P);
for(int i=0;i<P;i++){
char u[2];
char v[2];
int w;
int u1;
int v1;
scanf("%s%s%d",u,v,&w);
if(isupper(u[0])){
u1 = u[0]-'A' + 1;
}else{
u1 = u[0] -'a' + 27;
}
if(isupper(v[0])){
v1 = v[0] - 'A' + 1;
}else{
v1 = v[0] - 'a' + 27;
}
addedge(u1,v1,w);
addedge(v1,u1,w);
}
memset(dis,0x3f,sizeof(dis));
dis[26] = 0;
q.push(vertex(26,0));
vertex t;
while(!q.empty()){
t = q.top();
q.pop();
for(int i=head[t.v];i!=0;i=e[i].next){
if(dis[t.v]+e[i].value<dis[e[i].to]){
dis[e[i].to] = dis[t.v] + e[i].value;
q.push(vertex(e[i].to,dis[e[i].to]));
}
}
}
int ans = INT_MAX;
int site;
for(int i=1;i<=25;i++){
if(dis[i]<ans){
ans = dis[i];
site = i;
}
}
if(site>=1 && site<=26){
printf("%c %d\n",site+'A'-1,ans);
}else{
printf("%c %d\n",site+'a'-27,ans);
}
return 0;
}
P1629 邮递员送信
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
const int INF = 2147483647;
const int maxn = 1e5 + 5;
int dis[maxn];
bool vis[maxn];
vector<pair<int,int > > Gz[maxn],Gf[maxn];
priority_queue<pair<int,int> > q;
int n,m;
inline int read() {
int f = 1, val = 0;
char ch = getchar();
while((ch < '0' || ch > '9') && (ch!='-')) {
ch = getchar();
}
if(ch == '-') {
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
val = (val << 3) + (val << 1) +(ch - '0');
ch = getchar();
}
return val * f;
}
inline void dijstra1(int s){
for(int i=1;i<=n;i++){
dis[i] = INF;
vis[i] = false;
}
dis[s] = 0;
q.push(make_pair(-dis[s],s));
while(!q.empty()){
pair<int,int> x = q.top();
q.pop();
if(vis[x.second]){
continue;
}
int u = x.second;
vis[u] = true;
for(int j=0;j<Gf[u].size();j++){
int v = Gf[u][j].first;
if(dis[v]>dis[u] +Gf[u][j].second){
dis[v] = dis[u] + Gf[u][j].second;
q.push(make_pair(-dis[v],v));
}
}
}
}
inline void dijstra2(int s){
for(int i=1;i<=n;i++){
dis[i] = INF;
vis[i] = false;
}
dis[s] = 0;
q.push(make_pair(-dis[s],s));
while(!q.empty()){
pair<int,int> x = q.top();
q.pop();
if(vis[x.second]){
continue;
}
int u = x.second;
vis[u] = true;
for(int j=0;j<Gz[u].size();j++){
int v = Gz[u][j].first;
if(dis[v]>dis[u] +Gz[u][j].second){
dis[v] = dis[u] + Gz[u][j].second;
q.push(make_pair(-dis[v],v));
}
}
}
}
int main() {
n = read();
m = read();
for(register int i=0; i<m; i++) {
int u =read();
int v =read();
int w =read();
Gz[u].push_back(make_pair(v,w));
Gf[v].push_back(make_pair(u,w));
}
dijstra2(1);
long long sum = 0;
for(int i=2;i<=n;i++){
sum += dis[i];
}
dijstra1(1);
for(int i=2;i<=n;i++){
sum += dis[i];
}
cout << sum << endl;
return 0;
}
P2888 [USACO07NOV]Cow Hurdles S
算法标签: 图论 + Floyd
注意点: Floyd模板题!
#include<bits/stdc++.h>
using namespace std;
int N,M,T;
int dis[305][305];
int main(){
scanf("%d%d%d",&N,&M,&T);
memset(dis,0x3f,sizeof(dis));
for(int i=0;i<M;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
dis[u][v] = w;
}
for(int k=1;k<=N;k++){
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
if((i!=j) && (j!=k) && (k!=i)){
dis[i][j] = min(max(dis[i][k],dis[k][j]),dis[i][j]);
}
}
}
}
for(int i=0;i<T;i++){
int u,v;
scanf("%d%d",&u,&v);
if(dis[u][v]==0x3f3f3f3f){
printf("-1\n");
}else{
printf("%d\n",dis[u][v]);
}
}
return 0;
}
P4779 【模板】单源最短路径(标准版)
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2147483647;
struct node{
int v;
int dis;
};
node temp;
vector<node> G[100005];
int dp[100005];
bool vis[100005];
priority_queue<pair<int,int> > q;
void dijkstra(int s){
dp[s] = 0;
q.push(make_pair(-dp[s],s));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int v = f.second;
int dis = abs(f.first);
if(vis[v]){
continue;
}
vis[v] = true;
dp[v] = dis;
for(int i=0;i<G[v].size();i++){
node t = G[v][i];
int u = t.v;
int d = t.dis;
if(dp[v]+d<dp[u]){
dp[u] = dp[v] + d;
q.push(make_pair(-dp[u],u));
}
}
}
}
int main(){
memset(dp,0x3f,sizeof(dp));
int n,m,s;
cin >> n >> m >> s;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
temp.v = v;
temp.dis = w;
G[u].push_back(temp);
}
dijkstra(s);
for(int i=1;i<=n;i++){
printf("%d ",dp[i]);
}
return 0;
}
P2951 [USACO09OPEN]Hide and Seek S
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m;
int head[20005];
int cnt = 0;
int dis[20005];
bool vis[20005];
struct Edge{
int value;
int to;
int next;
};
Edge e[200005];
void addedge(int a,int b,int v){
cnt++;
e[cnt].value = v;
e[cnt].to = b;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
void SPFA(){
int u;
queue<int> q;
dis[1] = 0;
q.push(1);
vis[1] = true;
while(!q.empty()){
u = q.front();
vis[u] = false;
q.pop();
for(int i=head[u];i;i=e[i].next){
if(dis[u] + e[i].value < dis[e[i].to]){
dis[e[i].to] = dis[u] + e[i].value;
if(!vis[e[i].to]){
vis[e[i].to] = true;
q.push(e[i].to);
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v,1);
addedge(v,u,1);
}
memset(dis,0x3f,sizeof(dis));
SPFA();
int ans = INT_MIN;
int site = -1;
for(int i=1;i<=n;i++){
if(dis[i]!=0x3f3f3f3f){
if(dis[i]>ans){
ans = dis[i];
site = i;
}
}
}
int cnt = 0;
for(int i=1;i<=n;i++){
if(ans == dis[i]){
cnt++;
}
}
printf("%d %d %d\n",site,ans,cnt);
return 0;
}
P3905 道路重建
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m;
int G[105][105];
struct Edge{
int to;
int value;
int next;
};
Edge e[20005];
int cnt = 0;
int head[105];
int dis[105];
bool vis[105];
int k;
int A,B;
void addedge(int a,int b,int v){
cnt++;
e[cnt].to = b;
e[cnt].value = v;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
void SPFA(){
int u;
dis[A] = 0;
vis[A] = true;
queue<int> q;
q.push(A);
while(!q.empty()){
u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u];i;i=e[i].next){
if(dis[u] + e[i].value < dis[e[i].to]){
dis[e[i].to] = dis[u] + e[i].value;
if(!vis[e[i].to]){
vis[e[i].to] = true;
q.push(e[i].to);
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u][v] = G[v][u] = w;
addedge(u,v,0);
addedge(v,u,0);
}
scanf("%d",&k);
for(int i=1;i<=k;i++){
int u,v;
scanf("%d%d",&u,&v);
for(int j=head[u];j;j=e[j].next){
if(e[j].to == v){
e[j].value = G[u][v];
}
}
for(int j=head[v];j;j=e[j].next){
if(e[j].to == u){
e[j].value = G[u][v];
}
}
}
scanf("%d%d",&A,&B);
memset(dis,0x3f,sizeof(dis));
SPFA();
printf("%d\n",dis[B]);
return 0;
}
P1690 贪婪的Copy
算法标签: 图论 + Floyd + DFS
注意点: 由于题目中给出的点的数量为N(<100),因此可考虑floyd全源最短路径的方法,求出所有点之间的最短路,由于需要枚举的点数量P(<10),因此可采用DFS搜索的方法,枚举所有可能,求得最短路长度后输出!
#include<bits/stdc++.h>
using namespace std;
int N;
int P;
int G[105][105];
int a[15];
bool vis[15];
int ans = INT_MAX;
void dfs(int u,int sum,int depth){
if(depth == P){
ans = min(ans,sum+G[u][N]);
return;
}
for(int i=1;i<=P;i++){
if(!vis[i]){
vis[i] = true;
dfs(a[i],sum+G[u][a[i]],depth+1);
vis[i] = false;
}
}
}
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
scanf("%d",&G[i][j]);
}
}
scanf("%d",&P);
for(int i=1;i<=P;i++){
scanf("%d",&a[i]);
}
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],G[i][k]+G[k][j]);
}
}
}
dfs(1,0,0);
cout << ans << endl;
return 0;
}
P6833 [Cnoi2020]雷雨
算法标签: 图论 + Dijkstra + 枚举
注意点: 一道好题!(本题真实难度应该是绿色)
首先是解题思路,我们需要构造出一幅图,把每个格子看成顶点,将格子相邻的连接看作边,而边权对应着到达点的点权,初始时我们从一个点出发,将最短路长度更新为起点的点权,然后做dijkstra算法,那么需要做几次呢?由于题目中有3个初始的位置,可把路分成3段来计算,因此需要3次dijkstra算法,依次更新得到雷电、森林和木屋到格子中任何一个点的距离,目标函数为 d p 1 [ i ] + d p 2 [ i ] + d p 3 [ i ] − 2 ∗ a [ x ] [ y ] , ( x − 1 ) ∗ m + y = i dp1[i]+dp2[i]+dp3[i]-2*a[x][y],(x-1)*m+y=i dp1[i]+dp2[i]+dp3[i]−2∗a[x][y],(x−1)∗m+y=i(枚举的点权加了3遍,因此-2),我们枚举图中所有的点,求其距离和最小值即可
最后注意,不开long long一场空!100->30
#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,c;
int aa[1005][1005];
typedef long long LL;
struct node{
int v;
int w;
};
node temp;
const int maxn = 1e6+5;
vector<node> G[maxn];
bool vis[maxn];
LL dp1[maxn];
LL dp2[maxn];
LL dp3[maxn];
void dijkstra(int u,LL dp[],int x,int y){
memset(vis,0,sizeof(vis));
dp[u] = aa[x][y];
priority_queue<pair<LL,int> > q;
q.push(make_pair(-dp[u],u));
while(!q.empty()){
pair<LL,int> f = q.top();
q.pop();
int u = f.second;
if(vis[u]){
continue;
}
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].w;
if(!vis[v] && dp[u] + w < dp[v]){
dp[v] = dp[u] + w;
q.push(make_pair(-dp[v],v));
}
}
}
}
int main(){
memset(dp1,0x3f,sizeof(dp1));
memset(dp2,0x3f,sizeof(dp2));
memset(dp3,0x3f,sizeof(dp3));
scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&aa[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i!=n && j!=m){
int t1 = (i-1)*m + j;
int t2 = (i-1)*m + (j+1);
temp.v = t2;
temp.w = aa[i][j+1];
G[t1].push_back(temp);
temp.v = t1;
temp.w = aa[i][j];
G[t2].push_back(temp);
int t3 = i*m + j;
temp.v = t3;
temp.w = aa[i+1][j];
G[t1].push_back(temp);
temp.v = t1;
temp.w = aa[i][j];
G[t3].push_back(temp);
}else if(i==n && j!=m){
int t1 = (i-1)*m + j;
int t2 = (i-1)*m + (j+1);
temp.v = t2;
temp.w = aa[i][j+1];
G[t1].push_back(temp);
temp.v = t1;
temp.w = aa[i][j];
G[t2].push_back(temp);
}else if(i!=n && j==m){
int t1 = (i-1)*m + j;
int t3 = i*m + j;
temp.v = t3;
temp.w = aa[i+1][j];
G[t1].push_back(temp);
temp.v = t1;
temp.w = aa[i][j];
G[t3].push_back(temp);
}
}
}
dijkstra(a,dp1,1,a);
dijkstra((n-1)*m+b,dp2,n,b);
dijkstra((n-1)*m+c,dp3,n,c);
long long ans = LONG_LONG_MAX;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
long long tmp = (long long)dp1[(i-1)*m+j]
+ (long long)dp2[(i-1)*m+j]
+ (long long)dp3[(i-1)*m+j]
- 2LL*aa[i][j];
ans = min(ans,tmp);
}
}
cout << ans << endl;
return 0;
}
P6770 [USACO05MAR]Checking an Alibi 不在场的证明
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题
#include<bits/stdc++.h>
using namespace std;
int F,P,C,M;
struct node{
int v;
int w;
};
node temp;
vector<node> G[505];
int dp[505];
int a[105];
bool vis[505];
priority_queue<pair<int,int> > q;
void dijkstra(int u){
dp[u] = 0;
q.push(make_pair(-0,u));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int u = f.second;
if(vis[u]){
continue;
}
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int dis = G[u][i].w;
if(!vis[v] && dis+dp[u]<dp[v]){
dp[v] = dp[u] + dis;
}q.push(make_pair(-dp[v],v));
}
}
}
int main(){
memset(dp,0x3f,sizeof(dp));
scanf("%d%d%d%d",&F,&P,&C,&M);
for(int i=1;i<=P;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
temp.v = v;
temp.w = w;
G[u].push_back(temp);
temp.v = u;
G[v].push_back(temp);
}
for(int i=1;i<=C;i++){
scanf("%d",&a[i]);
}
dijkstra(1);
int ans = 0;
for(int i=1;i<=C;i++){
if(dp[a[i]]<=M){
ans++;
}
}
cout << ans << endl;
for(int i=1;i<=C;i++){
if(dp[a[i]]<=M){
printf("%d\n",i);
}
}
return 0;
}
P1342 请柬
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题,注意这里用的是链式前向星存储图,上一题模板采用的是邻接表!
#include<bits/stdc++.h>
using namespace std;
struct Edge{
long long value;
int to;
int next;
};
const int maxn = 1e6+10;
int n,m;
int cnt1 = 0;
int cnt2 = 0;
Edge e1[maxn];
Edge e2[maxn];
int head1[maxn];
int head2[maxn];
bool vis1[maxn];
bool vis2[maxn];
long long dis1[maxn];
long long dis2[maxn];
struct vertex{
long long dis;
int v;
vertex(){
}
vertex(int v1,long long d){
v = v1;
dis = d;
}
bool operator < (const vertex &x)const{
return x.dis < dis;
}
};
priority_queue<vertex> q1;
priority_queue<vertex> q2;
void addedge1(int a,int b,long long v){
cnt1++;
e1[cnt1].value = v;
e1[cnt1].to = b;
e1[cnt1].next = head1[a];
head1[a] = cnt1;
return;
}
void addedge2(int b,int a,long long v){
cnt2++;
e2[cnt2].value = v;
e2[cnt2].to = b;
e2[cnt2].next = head2[a];
head2[a] = cnt2;
return;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge1(u,v,w);
addedge2(u,v,w);
}
memset(dis1,0x3f,sizeof(dis1));
memset(dis2,0x3f,sizeof(dis2));
dis1[1] = 0;
dis2[1] = 0;
q1.push(vertex(1,0));
q2.push(vertex(1,0));
vertex t;
long long ans = 0;
while(!q1.empty()){
t = q1.top();
q1.pop();
if(!vis1[t.v]){
vis1[t.v] = true;
for(int i=head1[t.v];i!=0;i=e1[i].next){
if(dis1[t.v] + e1[i].value < dis1[e1[i].to]){
dis1[e1[i].to] = dis1[t.v] + e1[i].value;
q1.push(vertex(e1[i].to,dis1[e1[i].to]));
}
}
}
}
while(!q2.empty()){
t = q2.top();
q2.pop();
if(!vis2[t.v]){
vis2[t.v] = true;
for(int i=head2[t.v];i!=0;i=e2[i].next){
if(dis2[t.v] + e2[i].value < dis2[e2[i].to]){
dis2[e2[i].to] = dis2[t.v] + e2[i].value;
q2.push(vertex(e2[i].to,dis2[e2[i].to]));
}
}
}
}
for(int i=2;i<=n;i++){
ans += dis1[i];
}
for(int i=2;i<=n;i++){
ans += dis2[i];
}
printf("%lld\n",ans);
return 0;
}
P2648 赚钱
算法标签: 图论 + Floyd + 最小正环
注意点: 做两遍Floyd算法,如果第二遍最大值距离依然更新,说明存在一个正环,若无更新,则输出最大值点即可,下一题和本题重题
#include<bits/stdc++.h>
using namespace std;
int f[305][305];
int D,P,C,F;
void Floyd(){
for(int k=1;k<=C;k++){
for(int i=1;i<=C;i++){
for(int j=1;j<=C;j++){
f[i][j]=max(f[i][j],f[i][k]+f[k][j]);
}
}
}
}
int main(){
cin >> D >> P >> C >>F;
memset(f,0xc0c0c0c0,sizeof(f));
for(int i=0;i<P;i++){
int x,y;
cin >> x >> y;
f[x][y]=D;
}
for(int i=0;i<F;i++){
int x,y,t;
cin >> x >> y >> t;
f[x][y] = D-t;
}
Floyd();
int maxn1=0xc0c0c0c0;
int maxn2=0xc0c0c0c0;
for(int i=1;i<=C;i++){
for(int j=1;j<=C;j++){
maxn1 = max(maxn1,f[i][j]);
}
}
Floyd();
for(int i=1;i<=C;i++){
for(int j=1;j<=C;j++){
maxn2 = max(maxn2,f[i][j]);
}
}
if(maxn1!=maxn2){
cout <<"orz"<<endl;
}else{
cout << D+maxn1 <<endl;
}
return 0;
}
P1938 [USACO09NOV]Job Hunt S
算法标签: 图论 + Floyd + 最小正环
注意点: 和上一题P2648重题
#include<bits/stdc++.h>
using namespace std;
int f[305][305];
int D,P,C,F,S;
void Floyd(){
for(int k=1;k<=C;k++){
for(int i=1;i<=C;i++){
for(int j=1;j<=C;j++){
f[i][j]=max(f[i][j],f[i][k]+f[k][j]);
}
}
}
}
int main(){
cin >> D >> P >> C >>F >> S;
memset(f,0xc0c0c0c0,sizeof(f));
for(int i=0;i<P;i++){
int x,y;
cin >> x >> y;
f[x][y]=D;
}
for(int i=0;i<F;i++){
int x,y,t;
cin >> x >> y >> t;
f[x][y] = D-t;
}
Floyd();
int maxn1=0xc0c0c0c0;
int maxn2=0xc0c0c0c0;
for(int i=1;i<=C;i++){
for(int j=1;j<=C;j++){
maxn1 = max(maxn1,f[i][j]);
}
}
Floyd();
for(int i=1;i<=C;i++){
for(int j=1;j<=C;j++){
maxn2 = max(maxn2,f[i][j]);
}
}
if(maxn1!=maxn2){
cout << "-1" <<endl;
}else{
maxn1=0xc0c0c0c0;
for(int i=1;i<=C;i++){
maxn1 = max(maxn1,f[S][i]);
}
cout << D+maxn1 <<endl;
}
return 0;
}
P3371 【模板】单源最短路径(弱化版)
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2147483647;
struct node{
int v;
int dis;
};
node temp;
vector<node> G[10005];
int dp[10005];
bool vis[10005];
priority_queue<pair<int,int> > q;
void dijkstra(int s){
dp[s] = 0;
q.push(make_pair(-dp[s],s));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int v = f.second;
int dis = abs(f.first);
if(vis[v]){
continue;
}
vis[v] = true;
dp[v] = dis;
for(int i=0;i<G[v].size();i++){
node t = G[v][i];
int u = t.v;
int d = t.dis;
if(dp[v]+d<dp[u]){
dp[u] = dp[v] + d;
q.push(make_pair(-dp[u],u));
}
}
}
}
int main(){
fill(dp,dp+10005,maxn);
int n,m,s;
cin >> n >> m >> s;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
temp.v = v;
temp.dis = w;
G[u].push_back(temp);
}
dijkstra(s);
for(int i=1;i<=n;i++){
printf("%d ",dp[i]);
}
return 0;
}
P6464 传送门
算法标签: 图论 +Floyd
注意点: 这道题需要枚举每两对顶点,假设其路径连接为0,用这两个点去更新所有现有的最短路径长度,然后统计一个最小结果即可!
#include<bits/stdc++.h>
using namespace std;
int N,m;
int G[105][105];
int G2[105][105];
const int INF = 0x3f3f3f3f;
int main(){
scanf("%d%d",&N,&m);
memset(G,INF,sizeof(G));
for(int i=1;i<=N;i++){
G[i][i] = 0;
}
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u][v] = G[v][u] = w;
}
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],G[i][k]+G[k][j]);
}
}
}
int res = INT_MAX;
for(int p=1;p<=N-1;p++){
for(int q=p+1;q<=N;q++){
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
G2[i][j] = G[i][j];
}
}
G2[p][q] = G2[q][p] = 0;
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
G2[i][j] = min(G2[i][j],G2[i][p]+G2[p][j]);
}
}
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
G2[i][j] = min(G2[i][j],G2[i][q]+G2[q][j]);
}
}
int ans = 0;
for(int i=1;i<=N-1;i++){
for(int j=i+1;j<=N;j++){
ans += G2[i][j];
}
}
res = min(res,ans);
}
}
cout << res << endl;
return 0;
}
P3906 Geodetic集合
算法标签: 图论 + Dijkstra + DFS
注意点: Dijkstra + DFS模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m;
int k;
struct node{
int v;
int w;
};
node temp;
vector<node> G[45];
vector<int> pre[45];
set<int> s;
int dp[45];
bool vis[45];
void dijkstra(int u){
memset(vis,0,sizeof(vis));
memset(dp,0x3f,sizeof(dp));
dp[u] = 0;
priority_queue<pair<int,int> > q;
q.push(make_pair(-dp[u],u));
while(!q.empty()){
pair<int,int> f = q.top();
q.pop();
int u = f.second;
if(vis[u]){
continue;
}
vis[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i].v;
int w = G[u][i].w;
if(!vis[v] && dp[u] + w < dp[v]){
pre[v].clear();
pre[v].push_back(u);
dp[v] = dp[u] + w;
q.push(make_pair(-dp[v],v));
}else if(!vis[v] && dp[u] + w == dp[v]){
pre[v].push_back(u);
}
}
}
}
void dfs(int u,int v){
if(v == u){
s.insert(u);
return;
}
s.insert(v);
for(int i=0;i<pre[v].size();i++){
dfs(u,pre[v][i]);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
temp.v = v;
temp.w = 1;
G[u].push_back(temp);
temp.v = u;
G[v].push_back(temp);
}
scanf("%d",&k);
for(int i=1;i<=k;i++){
int u,v;
for(int j=1;j<=n;j++){
pre[j].clear();
}
s.clear();
scanf("%d%d",&u,&v);
dijkstra(u);
dfs(u,v);
for(auto it=s.begin();it!=s.end();it++){
printf("%d ",*it);
}
printf("\n");
}
return 0;
}
P1807 最长路
算法标签: 图论 + Dijkstra
注意点: Dijkstra模板题!
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct Edge{
int to;
int next;
int val;
};
Edge e[100005];
int head[1505];
int dis[1505];
int cnt = 0;
int vis[1505];
void addedge(int a,int b,int v){
cnt++;
e[cnt].to = b;
e[cnt].val = v;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
void SPFA(){
int u;
dis[1] = 0;
vis[1] = true;
queue<int> q;
q.push(1);
while(!q.empty()){
u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u];i;i=e[i].next){
if(dis[u] + e[i].val < dis[e[i].to]){
dis[e[i].to] = dis[u] + e[i].val;
if(!vis[e[i].to]){
vis[e[i].to] = true;
q.push(e[i].to);
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,-w);
}
memset(dis,0x3f,sizeof(dis));
SPFA();
if(dis[n]==0x3f3f3f3f){
printf("-1\n");
}else{
printf("%d\n",abs(dis[n]));
}
return 0;
}
P3385 【模板】负环
算法标签: 图论 + Bellman-Ford
注意点: Bellman-Ford模板题!
注意题目判断的是从1出发可达的负环,若从1出发不可达,则不必松弛该顶点
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct node{
int u;
int v;
int w;
};
node t[6005];
int dp[2005];
int cnt = 0;
bool Bellman(int u){
memset(dp,0x3f,sizeof(dp));
dp[u] = 0;
for(int i=1;i<=n-1;i++){
for(int j=0;j<cnt;j++){
int u = t[j].u;
int v = t[j].v;
int dis = t[j].w;
if(dp[u]!=0x3f3f3f3f && dp[u] + dis < dp[v]){
dp[v] = dp[u] + dis;
}
}
}
for(int j=0;j<cnt;j++){
int u = t[j].u;
int v = t[j].v;
int dis = t[j].w;
if(dp[u]!=0x3f3f3f3f && dp[u] + dis < dp[v]){
return true;
}
}
return false;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
cnt = 0;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
t[cnt].u = u;
t[cnt].v = v;
t[cnt++].w = w;
if(w>=0){
t[cnt].u = v;
t[cnt].v = u;
t[cnt++].w = w;
}
}
bool f = Bellman(1);
if(f){
printf("YES\n");
}else{
printf("NO\n");
}
}
return 0;
}
P2136 拉近距离
算法标签: 图论 + SPFA
注意点: SPFA模板题!接上一题,本题核心思想也是判断负环,此处用的是改进的Bellman-Ford算法,即SPFA算法
#include<bits/stdc++.h>
using namespace std;
int N,M;
struct Edge{
int to;
int next;
int value;
};
Edge e[20005];
int head[1005];
int dis[1005];
int dis1[1005];
int dis2[1005];
bool vis[1005];
int cnt = 0;
int ans[1005];
void addedge(int a,int b,int v){
cnt++;
e[cnt].value = v;
e[cnt].to = b;
e[cnt].next = head[a];
head[a] = cnt;
return;
}
bool SPFA(int s){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(ans,0,sizeof(ans));
int u;
dis[s] = 0;
vis[s] = true;
queue<int> q;
q.push(s);
while(!q.empty()){
u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u];i;i=e[i].next){
if(dis[u]+e[i].value<dis[e[i].to]){
dis[e[i].to] = dis[u] + e[i].value;
if(!vis[e[i].to]){
vis[e[i].to] = true;
ans[e[i].to]++;
q.push(e[i].to);
if(ans[e[i].to]>=N){
return true;
}
}
}
}
}
return false;
}
int main(){
scanf("%d%d",&N,&M);
for(int i=1;i<=M;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,-w);
}
bool f1 = SPFA(1);
for(int i=1;i<=N;i++){
dis1[i] = dis[i];
}
bool f2 = SPFA(N);
for(int i=1;i<=N;i++){
dis2[i] = dis[i];
}
if(f1 || f2){
printf("Forever love\n");
}else{
printf("%d\n",min(dis1[N],dis2[1]));
}
return 0;
}