题意:
有n个点m条边 n个点编号为1~n 每条边都有一个值 从一条边到另一条边,如果两边的指不同 花费就要+1,如果相同就不需要花费。 先从1走到n问最小花费是多少。(一开始从点1出来花费都为1)
思路:
总体而言是求最短路 然而普通的最短路在这里无法计算距离。 那么就用set【i】记录 当前到i这个点路径最短时 连向i的边有哪些 (因为可能有多种方案 到i 路径一样) 那么当 用 i来更新其他点j时 如果 i连向那个j的边在set【i】里出现过 那么就不需要花费,反之 花费+1 然后更新 点j取最优解 如果更新了最优解 放到优先队列里(不用优先队列会wa) 按花费最小排,每次拿最小花费出来更新其他点。
至于为什么不用优先队列会wa 这也就是说 为什么要先选花费最小的更新
先上代码 (其实是标程改了改 嘿嘿嘿)
#include <bits/stdc++.h>
#define rep(i,m,n) for(i=m;i<=n;i++)
#define rsp(it,s) for(set<int>::iterator it=s.begin();it!=s.end();it++)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define pi acos(-1.0)
#define pii pair<int,int>
#define Lson L, mid, rt<<1
#define Rson mid+1, R, rt<<1|1
const int maxn = 1e5 + 10;
using namespace std;
int n, m, k, t, h[maxn], tot, ans[maxn];
set<int>comp[maxn]; //记录到这个点的最短路上一条边有哪些
struct node
{
int to, nxt, com;
} e[maxn << 2];
void add(int x, int y, int z)
{
tot++;
e[tot].to = y;
e[tot].com = z;
e[tot].nxt = h[x];
h[x] = tot;
}
priority_queue<pair<int, int> >p;
int main()
{
int i, j;
while (scanf("%d %d", &n, &m) !=EOF)
{
for (int i = 1; i <= n; i++)
{
h[i] = -1;
ans[i] = inf;
comp[i].clear();
}
tot = 0;
while (m--)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
p.push({0, 1}); //从1这点开始 初始值为0
ans[1] = 0;
while (!p.empty())
{
int now = p.top().se, ca = -p.top().fi; //因为优先队列是从大到小排 那么要优先处理路径小的 就存负数进去
p.pop();
if (ans[now] < ca) //如果这个点又被更新过最优的话 这对pair就没意义了
{
continue;
}
for (i = h[now]; ~i; i = e[i].nxt)
{
int to = e[i].to, to_com = e[i].com; //to为下一个点 to_com为所连边的值
int to_ca = ca + (!comp[now].count(to_com));//如果到now这个点的最短路 的上一条边有to_com值出现过就加0 (set去重了)没有出现过就+1
if (ans[to] > to_ca) //如果有更优解就更新
{
ans[to] = to_ca;
p.push({ -to_ca, to});
comp[to].clear();
comp[to].insert(to_com);
}
else if (ans[to] == to_ca) //如果最优解相同 那么把边加入集合记下来
{
comp[to].insert(to_com);
}
}
}
printf("%d\n", ans[n] == inf ? -1 : ans[n]); //没找到就是原来的最大值 输出-1
}
//system("Pause");
return 0;
}
理解代码后 那么来说说如果不先选最小花费会发生什么
如图 有六个点 从1 开始 到6 那么按 假如1先走2
那么当从队列里取出4时 5还在队列里
那么这时 从4更新6 会发现没有相同边花费要加1 结果是 2
然后 5再更新4的set
最后 6没什么能更新了 队列也空了 就结束了
那么答案是2 很明显错了
如果2更新了4 后 再用5更新4 的set 那么
4更新6时会发现有相同边 花费+0 结果是 1
所以要先选花费小的更新
现在这个问题是 如果不排序 就会发生set集合不完整的问题
那么如果每更新一次set集合就将点再放入队列呢
恭喜你 样例就死循环了