YbtOJ 「图论」 第3章 最短路径
最短路径(有向图)
floyd
求多源最短路的常用方法 通过枚举中间的分界点来实现 复杂度
memset ( f , 0x3f , sizeof ( f ) );
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
图的传递闭包
指一个矩阵
dijkstra
单源最短路 基于贪心思想
【算法】最短路径查找—Dijkstra算法_哔哩哔哩_bilibili
先将初始所有点的距离设置无穷大 起点加入队列 { 然后把队列中里起点最短的点提出来 遍历这个点的所有出边 更新距离目标点的最短距离 一旦更新 就扔到队列中 } 只要队列不为空 就重复括号内的内容 始终每个点只会进出队列一次 只要一个点作为u节点更新过了 这个节点就永远不会作为u节点来使用
struct kk
{
int dis , id;
friend bool operator < ( const kk a , const kk b ) { return a.dis > b.dis; }
};
void dij ( )
{
memset ( dis , inf , sizeof dis );
priority_queue <kk> q;
dis[1] = 0;
q.push ( kk { dis[1] , 1 } );
while ( !q.empty() )
{
int u = q.top().id , f = q.top().dis; q.pop();
if ( f != dis[u] ) continue;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( dis[v] > dis[u] + e[i].w )
{
dis[v] = dis[u] + e[i].w;
q.push ( kk { dis[v] , v } );
}
}
}
}
spfa
单源最短路 但是时间复杂度在毒瘤数据面前可能退化到
算法思路:先将初始所有点的距离设置无穷大 用队列来保存待优化的结点 vis数组用于保存这个点是否在队列中 起点加入队列 优化时每次取出队首结点 并将vis[u]改为0 遍历这个节点的所有出边 如果可以更新最短路 就把这条边的终点加入队列中(如果队列中没有这个节点的话)
queue < int > q;
void spfa(int s)
{
for(int i=1;i<=n;i++) dis[i]=inf;
q.push(s);
dis[s]=0;vis[s]=1;
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w)
{
dis[v]=dis[u]+e[i].w;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
A. 【例题1】单源最短路径
[题目描述]
给定一个
数据保证你能从
[输入格式]
第一行为三个正整数
[输出格式]
输出一行
[代码实现]
#include <bits/stdc++.h>
using namespace std;
#define pii pair < int , int >
#define inl inline
#define mkp make_pair
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int n , m , s , dis[N];
int head[N] , cnt;
struct node { int to , nxt , w; } e[N<<1];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }
priority_queue < pii , vector<pii> , greater<pii> > q;
void dij ( int s )
{
memset ( dis , inf , sizeof dis );
dis[s] = 0;
q.push ( mkp(0,s) );
while ( !q.empty() )
{
int u = q.top().se , d = q.top().fi;
q.pop();
if ( d != dis[u] ) continue;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( dis[v] > dis[u] + e[i].w )
{
dis[v] = dis[u] + e[i].w;
q.push ( pii ( dis[v] , v ) );
}
}
}
}
signed main ()
{
n = read() , m = read() , s = read();
for ( int i = 1 , u , v , w ; i <= m ; i ++ ) u = read() , v = read() , w = read() , add ( u , v , w );
dij(s);
for ( int i = 1 ; i <= n ; i ++ ) printf ( "%d " , dis[i] );
return 0;
}
B. 【例题2】负环判断
[题目描述]
给定一个
负环的定义是:一条边权之和为负数的回路。
[输入格式]
本题单测试点有多组测试数据。
输入的第一行是一个整数
第一行有两个整数,分别表示图的点数
接下来
- 若
,则表示存在一条从 至 边权为 的边,还存在一条从 至 边权为 的边。 - 若
,则只表示存在一条从 至 边权为 的边。
[输出格式]
对于每组数据,输出一行一个字符串,若所求负环存在,则输出 YES
,否则输出 NO
。
[算法分析]
用spfa 对于每一个节点记录它的入队次数(如果记录松弛次数的话可能会有蜜汁错误) 如果大于
[代码实现]
#include <bits/stdc++.h>
using namespace std;
#define pii pair < int , int >
#define inl inline
#define mkp make_pair
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int n , m;
int head[N] , cnt;
struct node { int to , nxt , w; } e[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }
void init()
{
memset ( head , 0 , sizeof head );
cnt = 0;
}
queue <int> q;
int dis[N] , in[N] , vis[N] , h , t;
bool spfa ( int s )
{
memset ( dis , inf , sizeof dis );
memset ( in , 0 , sizeof in );
memset ( vis , 0 , sizeof vis );
q.push(s) , in[s] = 1 , dis[s] = 0;
while ( !q.empty() )
{
int u = q.front(); q.pop(); in[u] = 0;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( dis[v] > dis[u] + e[i].w )
{
dis[v] = dis[u] + e[i].w;
vis[v] = vis[u] + 1;//到这里的最短路经过的边的个数(也就是松弛次数)
if ( vis[v] >= n ) return 1;
if ( !in[v] ) q.push(v) , in[v] = 1;
}
}
}
return 0;
}
int main()
{
int T = read();
while ( T -- )
{
init();
n = read() , m = read();
for ( int i = 1 , u , v , w ; i <= m ; i ++ )
{
u = read() , v = read() , w = read();
add ( u , v , w );
if ( w >= 0 ) add ( v , u , w );
}
if ( spfa(1) ) printf ( "YE5\n" );
else printf ( "N0\n" );
}
return 0;
}
C. 【例题3】最优贸易
[题目描述]
$C
商人阿龙来到
假设
假设
阿龙可以选择如下一条线路:
阿龙也可以选择如下一条线路
现在给出 $n
[输入格式]
第一行包含
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。
接下来
[输出格式]
一个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出
[算法分析]
分层图最长路经典题目 将每一个点分成三层 第一层的
第二层的
上下两层之间不连双向边 这样保证了只能买入一次和卖出一次 最终答案就是
[代码实现]
#include <bits/stdc++.h>
using namespace std;
#define pii pair < int , int >
#define inl inline
#define mkp make_pair
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 5;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int n , m , a[N];
int head[N] , cnt;
struct node { int to , nxt , w; } e[N*3];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }
int dis[N] , in[N] , q[N] , t , h;
void spfa ( int s )
{
memset ( dis , -inf , sizeof dis );
memset ( in , 0 , sizeof in );
t = 0 , h = 1;
q[++t] = s , in[s] = 1 , dis[s] = 0;
while ( h <= t )
{
int u = q[h++]; in[u] = 0;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( dis[v] < dis[u] + e[i].w )
{
dis[v] = dis[u] + e[i].w;
if ( !in[v] ) { in[v] = 1 , q[++t] = v; }
}
}
}
}
signed main ()
{
n = read() , m = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , add ( i , i + n , - a[i] ) , add ( i + n , i + 2 * n , a[i] );
for ( int i = 1 , u , v , w ; i <= m ; i ++ )
{
u = read() , v = read() , w = read();
add ( u , v , 0 ) , add ( u + n , v + n , 0 ) , add ( u + 2 * n , v + 2 * n , 0 );
if ( w == 2 ) add ( v , u , 0 ) , add ( v + n , u + n , 0 ) , add ( v + 2 * n , u + 2 * n , 0 );
}
spfa(1);
printf ( "%d" , max ( dis[n] , dis[3*n] ) );
return 0;
}
D. 【例题4】汽车加油
[题目描述]
给定一个
一辆汽车从起点◎出发驶向右下角终点▲,其坐标为
在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:
-
汽车只能沿网格边行驶,装满油后能行驶
条网格边。出发时汽车已装满油,在起点与终点处不设油库。 -
汽车经过一条网格边时,若其
坐标或 坐标减小,则应付费用 ,否则免付费用。 -
汽车在行驶过程中遇油库则应加满油并付加油费用
。 -
在需要时可在网格点处增设油库,并付增设油库费用
(不含加油费用 )。 -
均为正整数, 且满足约束: 。
设计一个算法,求出汽车从起点出发到达终点所付的最小费用。
[输入格式]
文件的第一行是
第二行起是一个
方阵的第
[输出格式]
程序运行结束时,输出最小费用。
[算法分析]
分层图最短路 对于每一个点 都有油箱的使用油量 当这个油量为
[代码实现]
#include <bits/stdc++.h>
using namespace std;
#define pii pair < int , int >
#define inl inline
#define mkp make_pair
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 5;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int n , K , A , B , C;
int head[N] , cnt;
struct node { int to , nxt , w; } e[N*2];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }
int dis[N] , in[N] , q[20000000] , t , h , ans = inf;
void spfa ( int s )
{
memset ( dis , inf , sizeof dis );
memset ( in , 0 , sizeof in );
t = 0 , h = 1;
q[++t] = s , in[s] = 1 , dis[s] = 0;
while ( h <= t )
{
int u = q[h++]; in[u] = 0;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( dis[v] > dis[u] + e[i].w )
{
dis[v] = dis[u] + e[i].w;
if ( !in[v] ) { in[v] = 1 , q[++t] = v; }
}
}
}
}
int g ( int i , int j , int k ) { return ( i - 1 ) * n + j + k * n * n; }
int check ( int i , int j ) { return 1 <= i && i <= n && 1 <= j && j <= n; }
signed main ()
{
n = read() , K = read() , A = read() , B = read() , C = read();
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 , op ; j <= n ; j ++ )
{
op = read();
if ( op )//强制加油 只能从0连到1 其他点是不可能向外面连边的
{
for ( int k = 1 ; k <= K ; k ++ ) add ( g(i,j,k) , g(i,j,0) , A );
if ( check ( i + 1 , j ) ) add ( g(i,j,0) , g(i+1,j,1) , 0 );
if ( check ( i - 1 , j ) ) add ( g(i,j,0) , g(i-1,j,1) , B );
if ( check ( i , j + 1 ) ) add ( g(i,j,0) , g(i,j+1,1) , 0 );
if ( check ( i , j - 1 ) ) add ( g(i,j,0) , g(i,j-1,1) , B );
}
else
{
for ( int k = 1 ; k <= K ; k ++ ) add ( g(i,j,k) , g(i,j,0) , A + C );
for ( int k = 0 ; k < K ; k ++ )
{
if ( check ( i + 1 , j ) ) add ( g(i,j,k) , g(i+1,j,k+1) , 0 );
if ( check ( i - 1 , j ) ) add ( g(i,j,k) , g(i-1,j,k+1) , B );
if ( check ( i , j + 1 ) ) add ( g(i,j,k) , g(i,j+1,k+1) , 0 );
if ( check ( i , j - 1 ) ) add ( g(i,j,k) , g(i,j-1,k+1) , B );
}
}
}
spfa(g(1,1,0));
for ( int k = 1 ; k <= K ; k ++ ) ans = min ( ans , dis[g(n,n,k)] );
printf ( "%d" , ans );
return 0;
}
E. 1.最小花费
设置
变量定义一定要定义明白 cnt和cntt区分开!!!
#include <bits/stdc++.h>
using namespace std;
#define inl inline
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int dis[N][12] , vis[N][12] , s , t , n , m , kk , minn = inf;
int head[N] , cnt;
struct edge { int to , nxt , w; } e[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w } , head[u] = cnt; }
struct node
{
int pos , d , cnt;
friend bool operator < ( node a , node b ) { return a.d > b.d; }
};
priority_queue <node> q;
void dij ()
{
memset ( dis , inf , sizeof dis );
dis[s][0] = 0;
q.push ( (node) { s , 0 , 0 } );
while ( !q.empty() )
{
node temp = q.top(); q.pop();
int u = temp.pos , cntt = temp.cnt;
if ( vis[u][cntt] ) continue;
vis[u][cntt] = 1;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( dis[v][cntt+1] > dis[u][cntt] && cntt < kk )
{
dis[v][cntt+1] = dis[u][cntt];
q.push ( (node) { v , dis[v][cntt+1] , cntt + 1 } );
}
if ( dis[v][cntt] > dis[u][cntt] + e[i].w )
{
dis[v][cntt] = dis[u][cntt] + e[i].w;
q.push ( (node) { v , dis[v][cntt] , cntt } );
}
}
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , kk = read() , s = read() , t = read();
for ( int i = 1 ; i <= m ; i ++ )
{
int u = read() , v = read() , w = read();
add ( u , v , w ) , add ( v , u , w );
}
dij();
for ( int i = 0 ; i <= kk ; i ++ ) minn = min ( minn , dis[t][i] );
cout << minn << endl;
return 0;
}
F. 2.修建道路
维护一个
假做法:二分编号 但因为免费修建是双向边
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define mid ((l+r)>>1)
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int val[N] , n , m , k , l , r = -inf;
int head[N] , cnt;
struct edge { int to , nxt , w; } e[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w } , head[u] = cnt; }
int check ( int x )
{
memset ( val , inf , sizeof val );
priority_queue <pii> q;
val[1] = 0;
q.push ( mkp ( 0 , 1 ) );
while ( !q.empty() )
{
int u = q.top().se , f = q.top().fi; q.pop();
if ( f != val[u] ) continue;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( val[v] > val[u] + ( e[i].w > x ) )
{
val[v] = val[u] + ( e[i].w > x );
q.push ( mkp ( val[v] , v ) );
}
}
}
return val[n] <= k;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , k = read();
for ( int i = 1 ; i <= m ; i ++ )
{
int u = read() , v = read() , w = read();
add ( u , v , w ) , add ( v , u , w );
r = max ( r , w );
}
if ( !check(r) ) { cout << "-1" << endl; return 0; }
while ( l <= r )
{
if ( check(mid) ) r = mid - 1;
else l = mid + 1;
}
cout << l << endl;
return 0;
}
G. 3.糖果分配
差分约束经典模板
-
我们对于
的这样一个关系 化简之后可以得到 那么我们可以从 向 连边权为 的边表示这种关系 跑最长路无解:有正环即为无解 因为此时
和 都可以取得无穷小 -
如果是
的关系 那么我们跑最短路无解:有负环即为无解
最大值求最短路 最小值求最长路
对于本题而言 选择第一种方式即可
在这里用
对于
一定要注意 每个点的初始值必须为1
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , val[N] , ans;
int head[N] , cnt;
int head1[N] , cnt1;
struct node { int to , nxt , w ; } e[N] , e1[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }
void add1 ( int u , int v , int w ) { e1[++cnt1] = { v , head1[u] , w }; head1[u] = cnt1; }
int dfn[N] , low[N] , timer;
int scc[N] , id[N] , tot;
int in[N] , sta[100000000] , tp;
void tarjan ( int u )
{
dfn[u] = low[u] = ++timer;
sta[++tp] = u , in[u] = 1;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( !dfn[v] ) tarjan(v) , low[u] = min ( low[u] , low[v] );
else if ( in[v] ) low[u] = min ( low[u] , dfn[v] );
}
if ( dfn[u] == low[u] )
{
tot ++;
while ( tp )
{
int x = sta[tp--];
id[x] = tot , in[x] = 0 , scc[tot] ++;
if ( x == u ) break;
}
}
}
signed main ()
{
n = read() , m = read();
for ( int i = 1 , op , a , b ; i <= m ; i ++ )
{
op = read() , a = read() , b = read();
if ( op == 1 ) add ( a , b , 0 ) , add ( b , a , 0 );
else if ( op == 2 ) add ( a , b , 1 );
else if ( op == 3 ) add ( b , a , 0 );
else if ( op == 4 ) add ( b , a , 1 );
else add ( a , b , 0 );
}
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i);
for ( int u = 1 ; u <= n ; u ++ )
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( id[u] != id[v] ) add1 ( id[u] , id[v] , e[i].w );
else if ( e[i].w == 1 ) { cout << "-1" << endl; return 0; }//自己和自己的差为1显然不合法
}
for ( int i = 1 ; i <= tot ; i ++ ) val[i] = 1;//
for ( int u = tot ; u ; u -- )
{
ans += val[u] * scc[u];
for ( int i = head1[u] ; i ; i = e1[i].nxt )
{
int v = e1[i].to;
val[v] = max ( val[v] , val[u] + e1[i].w );
}
}
cout << ans << endl;
return 0;
}
H. 4.比较大小
floyd维护即可 注意无解情况的判断
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define mid ((l+r)>>1)
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int f[N][N] , n , m , q;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
m = read() , n = read() , q = read();
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , f[u][v] = 1;
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= n ; j ++ )
for ( int k = 1 ; k <= n ; k ++ )
f[i][j] |= f[i][k] & f[k][j];
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1; j <= n ; j ++ )
if ( i != j && f[i][j] && f[j][i] )
{ cout << "10000words to copy" << endl; return 0; }
for ( int i = 1 ; i <= q ; i ++ )
{
int l = read() , r = read();
if ( f[l][r] ) cout << "YES" << endl;
else if ( f[r][l] ) cout << "NO" << endl;
else cout << "DK" << endl;
}
return 0;
}
I. 5.删边问题
考虑二分答案 先将所有边按照价值排序 然后二分一个边的价值 将所有小于这个边的所有边都忽略 跑最短路即可
二分有两种策略:
-
二分编号
-
er
满足二分答案的原因:因为加一个边一定不会让最短路变长 减一个边一定不会让最短路变短
用了就90
二分编号代码:
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define mid ((l+r)>>1)
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int n , m , T , dis[N];
struct node { int u , v , len , val; } a[N];
int head[N] , cnt;
struct edge { int to , nxt , w , val; } e[N];
void add ( int u , int v , int w , int val ) { e[++cnt] = { v , head[u] , w , val } , head[u] = cnt; }
struct kk
{
int dis , id;
friend bool operator < ( const kk a , const kk b ) { return a.dis > b.dis; }
};
int check ( int x )
{
memset ( dis , inf , sizeof dis );
priority_queue <kk> q;
dis[1] = 0;
q.push ( kk { dis[1] , 1 } );
while ( !q.empty() )
{
int u = q.top().id , f = q.top().dis; q.pop();
if ( f != dis[u] ) continue;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
if ( e[i].val <= x ) continue;
int v = e[i].to;
if ( dis[v] > dis[u] + e[i].w )
{
dis[v] = dis[u] + e[i].w;
q.push ( kk { dis[v] , v } );
}
}
}
return dis[n] >= T;//最短路合法 尝试减小最大价值
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , T = read();
for ( int i = 1 ; i <= m ; i ++ ) a[i].u = read() , a[i].v = read() , a[i].len = read() , a[i].val = read();
sort ( a + 1 , a + m + 1 , [](const node a , const node b) { return a.val < b.val; } );
for ( int i = 1 ; i <= m ; i ++ ) add ( a[i].u , a[i].v , a[i].len , i );
if ( check(0) ) { cout << "-1" << ' ' << dis[n] << endl; return 0; }
int l = 1 , r = m;
while ( l <= r )
{
if ( check(mid) ) r = mid - 1;
else l = mid + 1;
}
cout << a[l].val << endl;
return 0;
}
二分权值代码:
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define mid ((l+r)>>1)
#define int long long
const int N = 2e6 + 5;
const int inf = 0x3f3f3f3f;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int n , m , T , l = 0 , r = -inf, dis[N];
int head[N] , cnt;
struct node { int to , nxt , w , val; } e[N];
void add ( int u , int v , int w , int val ) { e[++cnt] = { v , head[u] , w , val }; head[u] = cnt; }
struct kk
{
int dis , id;
friend bool operator < ( kk a , kk b ) { return a.dis > b.dis; }
};
int check ( int x )//小于等于这个边权的都删掉 跑最短路
{
memset ( dis , inf , sizeof dis );
priority_queue <kk> q;
dis[1] = 0;
q.push ( { dis[1] , 1 } );
while ( !q.empty() )
{
int u = q.top().id , f = q.top().dis; q.pop();
if ( f != dis[u] ) continue;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( e[i].val <= x ) continue;
if ( dis[v] > dis[u] + e[i].w )
{
dis[v] = dis[u] + e[i].w;
q.push ( { dis[v] , v } );
}
}
}
return dis[n] >= T;//路径长度符合要求 尝试减小权值
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , T = read();
for ( int i = 1 , u , v , len , val ; i <= m ; i ++ ) u = read() , v = read() , len = read() , val = read() , add ( u , v , len , val ) , r = max ( val , r );
if ( check(0) ) { cout << -1 << ' ' << dis[n] << endl; return 0; }
while ( l <= r )
{
if ( check(mid) ) r = mid - 1;
else l = mid + 1;
}
cout << l << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构