hdu 6386 Age of Moyu (求最短路 优先队列)

hdu 6386 Age of Moyu (求最短路 优先队列)_第1张图片

 题意:

有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 
#define rep(i,m,n) for(i=m;i<=n;i++)
#define rsp(it,s) for(set::iterator it=s.begin();it!=s.end();it++)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define vi vector
#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
#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];
setcomp[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 >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;
}

理解代码后 那么来说说如果不先选最小花费会发生什么

hdu 6386 Age of Moyu (求最短路 优先队列)_第2张图片

如图  有六个点 从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集合就将点再放入队列呢

恭喜你  样例就死循环了

你可能感兴趣的:(图论)