P8724 [蓝桥杯 2020 省 AB3] 限高杆 题解
题目传送门
题目大意:
给定一个
大致思路:
和先前的最短路一样建边,再存一下有没有限高杆,图就建好了。注意要建双向边,接下来就是遍历图,求出拆除一个或两个限高杆的最短距离,用没有拆除限高杆的最短距离减去即可。
本题核心:
如何求拆除一个或两个限高杆的最短距离。
我们可以用一个计数器,记录当前拆了多少个限高杆,再判断一下当前是否可以拆杆,加上最短路的判断。用封装结构体存下来,按走的道路的长度排下序,跑一遍 dij 即可。一些小细节看代码注释。
核心代码:
void dij()
{
memset(dis , 0x3f , sizeof(dis)); //初始化
dis[1][0] = 0; //设一维为x,二维为y,指的是拆了y个杆后到x的最短距离
q.push(Node(1 , 0 , 0)); //封装结构体小根堆
while(!q.empty())
{
int x = q.top().x; //x是当前节点
int k = q.top().k; //k是当前拆了多少个杆
q.pop();
if(vis[x][k]) continue; // vis数组含义和dis数组一样,存的是是否访问过
vis[x][k] = 1;
for(register int i = head[x] ; i ; i = e[i].net) // 链式前向星遍历
{
int to = e[i].to , w = e[i].w , f = e[i].xz; // f是是否有杆,有则k加1
if(k + f <= 2 && dis[x][k] + w < dis[to][k + f]) //当前拆的杆不能超过限制
{
dis[to][k + f] = dis[x][k] + w;
q.push(Node(to , k + f , dis[to][k + f]));
}
}
}
}
代码如下:
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
inline int read()
{
int s = 0 , w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();};
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();};
return s * w;
}
int n , m , cnt , sum1 , sum2;
struct node
{
int to , net , w , xz;
node(){xz = 0;} //初始化标记数组
}e[(int)2e5 + 10];
int head[(int)1e4 + 10] , dis[(int)1e4 + 10][5] , vis[(int)1e4 + 10][5];
struct Node // 封装结构体小根堆
{
int x , k , v;
Node(int X , int K , int V)
{x = X , k = K , v = V;}
};
bool operator<(const Node &ix , const Node &iy)
{
return ix.v > iy.v;
}
priority_queue <Node> q;
inline void add(int x , int y , int z , int f) //链式前向星存图,也可以用vector存
{
e[++cnt].w = z;
e[cnt].to = y;
e[cnt].xz = f;
e[cnt].net = head[x];
head[x] = cnt;
}
void dij()
{
memset(dis , 0x3f , sizeof(dis)); //初始化
dis[1][0] = 0; //设一维为x,二维为y,指的是拆了y个杆后到x的最短距离
q.push(Node(1 , 0 , 0)); //封装结构体小根堆
while(!q.empty())
{
int x = q.top().x; //x是当前节点
int k = q.top().k; //k是当前拆了多少个杆
q.pop();
if(vis[x][k]) continue; //vis数组含义和dis数组一样,存的是是否访问过
vis[x][k] = 1;
for(register int i = head[x] ; i ; i = e[i].net) //链式前向星遍历
{
int to = e[i].to , w = e[i].w , f = e[i].xz; // f是是否有杆,有则k加1
if(k + f <= 2 && dis[x][k] + w < dis[to][k + f]) //当前拆的杆不能超过限制
{
dis[to][k + f] = dis[x][k] + w;
q.push(Node(to , k + f , dis[to][k + f]));
}
}
}
}
int main()
{
n = read() , m = read();
for(register int i = 1 ; i <= m ; i++)
{
int u = read() , v = read() , z = read() , f = read();
add(u , v , z , f); //建双向边
add(v , u , z , f);
}
dij();
cout << dis[n][0] - min(dis[n][1] , dis[n][2]); //有可能有只有一个杆的情况,取最小值
return 0;
}