340. 通信线路

分层图做法

建k+1层图,相邻两层图之间权值为0,代表免费升级,在一层图里面跑就去找最大值,最后的答案就是第k+1层的dis[n]也就是dis[(k+1)*n],注意的是这样的做法只有在边数大于k时成立,当全部边都可以免费升级时,会出现问题,还没有跑到最后一层图就到达终点了,这时就会往回跑,导致边权增加,就会出错,所以需要把层与层之间的终点连接起来,使它们可以免费互相到达

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <bits/stdc++.h>
#define debug freopen("in.txt","r",stdin); freopen("out.txt","w",stdout)
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN=1e6+10;
const int MOD=1e9+7;
const int INF=0x3f3f3f3f;
const int SUB=-0x3f3f3f3f;
const double eps=1e-4;
const double E=exp(1);
const double pi=acos(-1);
struct node{
int to,next,w;
}e[10000010];
struct Node{
int u,d;
bool operator<(const Node &o)const{
return d>o.d;
}
};
int head[MAXN];
int dis[MAXN];
int n,p,k,tot;
priority_queue<Node> pq;
void add(int u,int v,int w){
e[tot]={v,head[u],w};
head[u]=tot++;
}
void dij(int s){
memset(dis,0x3f,sizeof dis);
pq.push({s,0});
dis[s]=0;
while(!pq.empty()){
Node now=pq.top();
pq.pop();
for(int i=head[now.u];~i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(dis[v]>max(dis[now.u],w)){
dis[v]=max(dis[now.u],w);
pq.push({v,dis[v]});
}
}
}
}
int main(){
ios;
memset(head,-1,sizeof head);
cin>>n>>p>>k;
while(p--){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
for(int j=1;j<=k;j++){
add(u+(j-1)*n,v+j*n,0);
add(v+(j-1)*n,u+j*n,0);
add(u+j*n,v+j*n,w);
add(v+j*n,u+j*n,w);
}
}
for(int i=1;i<=k;i++){
add(i*n,(i+1)*n,0); //把终点连成一体
}
dij(1);
if(dis[(k+1)*n]!=INF)cout<<dis[(k+1)*n]<<'\n';
else cout<<-1<<'\n';
return 0;
}

二分做法

题目求的就是第k+1大最小,一看求较大值最小,就要想到二分,二分答案,验证答案,标记大于验证值的边权为1,小于等于的为0,求起点到终点的最短路径,路径和小于等于k时即满足要求,否则不满足

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <bits/stdc++.h>
#define debug freopen("in.txt","r",stdin); freopen("out.txt","w",stdout)
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN=1e6+10;
const int MOD=1e9+7;
const int INF=0x3f3f3f3f;
const int SUB=-0x3f3f3f3f;
const double eps=1e-4;
const double E=exp(1);
const double pi=acos(-1);
struct node{
int to,next,w;
}e[MAXN];
struct Node{
int u,d;
bool operator<(const Node &o)const{
return d>o.d;
}
};
int head[MAXN],dis[MAXN];
int n,p,k,tot;
priority_queue<Node> pq;
void add(int u,int v,int w){
e[tot]={v,head[u],w};
head[u]=tot++;
}
bool check(int x){
memset(dis,0x3f,sizeof dis);
dis[1]=0;
pq.push({1,0});
while(!pq.empty()){
Node now=pq.top();
pq.pop();
for(int i=head[now.u];~i;i=e[i].next){
int v=e[i].to,w=e[i].w>x?1:0;
if(dis[v]>dis[now.u]+w){
dis[v]=dis[now.u]+w;
pq.push({v,dis[v]});
}
}
}
if(dis[n]<=k) return 1;
return 0;
}
int main(){
ios;
memset(head,-1,sizeof head);
cin>>n>>p>>k;
while(p--){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
int l=0,r=1000000,ans=INF,mid;
while(r>=l){
mid=(l+r)>>1;
if(check(mid)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
if(ans!=INF) cout<<ans<<'\n';
else cout<<-1<<'\n';
return 0;
}

动态规划做法

从起点到终点的一条路径中最多有k条免费升级,那给dis数组多加一维表示从起点到这个点已经免费升级几条边了,数组值依旧表示这个状态需要升级的最大值

转移方程:

若在这条边不使用机会:dis[y, p] = min(dis[y, p], max(dis[x, p], w))
若在这条边使用机会:dis[y, p+1] = min(dis[y, p+1], dis[x, p])

题解中很多都用SPFA,但这道题目都是正权,dijistla就可以,省了常数的时间,更新dis数组时变成转移方程就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
#define debug freopen("in.txt","r",stdin); freopen("out.txt","w",stdout)
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN=1e6+100;
const int MOD=1e9+7;
const int INF=0x3f3f3f3f;
const int SUB=-0x3f3f3f3f;
const double eps=1e-4;
const double E=exp(1);
const double pi=acos(-1);
struct node{
int to,next,w;
}e[MAXN];
struct Node{
int u,c;
};
int head[MAXN],dis[1100][1010];
int n,m,tot,k;
queue<Node> pq;
void add(int u,int v,int w){
e[tot]={v,head[u],w};
head[u]=tot++;
}
int dij(int s){
memset(dis,0x3f,sizeof dis);
pq.push({s,0});
dis[s][0]=0;
while(!pq.empty()){
Node now=pq.front();
pq.pop();
for(int i=head[now.u];~i;i=e[i].next){
int v=e[i].to,w=e[i].w;
if(dis[v][now.c]>max(dis[now.u][now.c],w)){
dis[v][now.c]=max(dis[now.u][now.c],w);
pq.push({v,now.c});
}
if(now.c+1<=k && dis[v][now.c+1]>dis[now.u][now.c]){
dis[v][now.c+1]=dis[now.u][now.c];
pq.push({v,now.c+1});
}
}
}
return 1;
}
int main(){
ios;
memset(head,-1,sizeof head);
cin>>n>>m>>k;
while(m--){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dij(1);
int ans=INF;
for(int i=0;i<=k;i++){
ans=min(ans,dis[n][i]);
}
if(ans!=INF)cout<<ans<<'\n';
else cout<<-1<<'\n';
return 0;
}