版权声明:本文为博主原创文章,喜欢就点个赞吧 https://blog.csdn.net/Anxdada/article/details/81903635
传送门
题意: 有一个n个点m条边的无向图, 边上有一个编号, 表示这条边归编号公司管理, 如果相邻的两条边的编号相同, 则代价为0. 否则代价加1, 其实为1, 问1到n的最低代价为多少.
思路: 这道题的思路真的是秒啊!!! 很明显我们要解决的问题就是如果让相同公司之间的花费为0, 所以我们进行拆点, 将u v c, 拆成u - uc, uc - vc, vc - v, 边权分别是1, 0, 1, 这样就可以让相邻的相同的公司的边之间的花费为0了. 比如:
将边拆掉后重建的图:
然后这样直接跑新图1-n的最短路然后/2就是答案. 理解的话就是在起点站上车需要以代价, 中间如果是相同公司则不需要花费代价, 下车有需要一个代价, 所以最后答案要/2.
更直接的样例就是:
3 2
1 2 1
2 3 1
新图为:
1 - (1, 1) - (2, 1) - 2
(2, 1) - (3, 1) - 3
所以就是通过1 -> (1, 1) -> (2, 1) -> (3, 1) - 3. 代价为2, /2 = 1就是答案
注意这样拆点后,边和点都会变得比较多. 范围要开大点. 然后建好跑最短路即可.
AC Code
const int maxn = 1e6+5;
int n, m;
struct node {
int to, next, w;
bool operator < (const node& a) const {
return w > a.w;
}
} e[maxn<<1];
int cnt, head[maxn];
void add(int u, int v, int w) {
e[cnt] = node{v, head[u], w};
head[u] = cnt++;
}
bool vis[maxn];
int dis[maxn];
void dij(int st, int ed) {
priority_queue<node> q;
Fill(dis,inf); Fill(vis,0);
dis[st] = 0;
q.push(node{st, 0, 0});
while (!q.empty()) {
node u = q.top();
q.pop();
if(vis[u.to]) continue;
vis[u.to] = 1;
for (int i = head[u.to]; ~i; i = e[i].next) {
int to = e[i].to;
if (dis[to] > dis[u.to] + e[i].w) {
dis[to] = dis[u.to] + e[i].w;
q.push(node{to, 0, dis[to]});
}
}
}
printf("%d\n", dis[ed] == inf?-1:dis[ed]/2);
}
map<int, int>mp[maxn];
void init() {
cnt = 0; Fill(head, -1);
for (int i = 1 ; i <= n ; i ++) {
mp[i].clear();
}
}
int getid(int x, int y) {
if (!mp[x][y]) mp[x][y] = ++n;
return mp[x][y];
}
void solve() {
while(~scanf("%d%d", &n, &m)) {
init(); int ed = n;
for (int i = 1 ; i <= m ; i ++) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
int uc = getid(u, c);
int vc = getid(v, c);
add(uc, vc, 0); add(vc, uc, 0);
add(u, uc, 1); add(uc, u, 1);
add(v, vc, 1); add(vc, v, 1);
}
dij(1, ed);
}
}