一本通 P1486 【黑暗城堡】


思路 :这道题既然要求使加入生成树中的点到1号节点的距离最小,那么我们可以理解为题目要求一个最短路径生成树,那么我们可以从1号节点向每个节点跑一遍SPFA最短路,并记录下来。这道题中如果满足两个点i,j && dis[i] = dis[j] + f[i][j](dis[i]表示第i个点到1号节点的最短距离,f[i][j]表示i向j连的直接路径的长度)那么就满足题目中的条件(生成树中的某个点到1号节点的路径等于从当前点到1号节点的最短路径),所以当前点的答案数++,最后再用乘法原理把他们的方案数乘起来。

证明一下为什么 dis[i] = dis[j] + f[i][j] 答案数就++ :

如样例图所示:

我们手动模拟求得dis[1] = 0, dis[2] = 1, dis[3] = 2,  dis[4] = 3(f的就不求了)

开始模拟:

  • 第2号节点 :枚举所有点,显而易见只有dis[2] = dis[1] + f[2][1]的这种情况(每个节点都会有这种情况的,即直接走最短路)
  • 第3号节点 :枚举所有点,显而易见有两种情况,一是直接走最短路(这里就不写了),二是经过2号节点,再到达1号节点dis[3] = dis[2] + f[3][2]
  • 第4号节点 :枚举所有点,显而易见有三种情况,一是直接走最短路(这里就不写了),二是经过2号节点,再到达1号节点dis[4] = dis[2] + f[4][2],三是经过3号节点,再到达1号节点dis[4] = dis[3] + f[4][3]

综上所述,如果一个节点可以经过另一个节点到达1号节点,而且距离还是相同的,那么答案数就可以++了。这里采用了Floyed的思想

那个式子翻译成中文就是如果一个节点到1号节点的最短路 = 另一个和它有连边的节点到根节点的最短路 + 它们两个节点之间的直接距离,那么答案数++,而那条边要不然就是在最短路里,要不然就是另一种方案

code :

 1 #include <bits/stdc++.h>
 2 #define INF 0x3f3f3f3f
 3 using namespace std;
 4 const long long MOD = pow(2, 31) - 1;//别忘了取膜 
 5 int n, m, x, y, num, head[1000001], vis[1000001];
 6 long long ans, z, dis[1000001], f[1001][1001];//这些变量最好开long long 
 7 struct node
 8 {
 9     int next, to;
10     long long val;
11 }stu[1000001];
12 inline void add(int x, int y, int z)//标准链式向前星 
13 {
14     stu[++num].next = head[x];
15     stu[num].to = y;
16     stu[num].val = z;
17     head[x] = num;
18     return;
19 }
20 inline void spfa(int s)//SPFA最短路 
21 {
22     memset(vis, 0, sizeof(vis));
23     memset(dis, INF, sizeof(dis));
24     queue < int > pru;
25     pru.push(s);
26     dis[s] = 0;
27     vis[s] = 1;
28     while(!pru.empty())
29     {
30         int u = pru.front();
31         pru.pop();
32         vis[u] = 0;
33         for(register int i = head[u]; i; i = stu[i].next)
34         {
35             int k = stu[i].to;
36             if(dis[k] > dis[u] + stu[i].val)
37             {
38                 dis[k] = dis[u] + stu[i].val;
39                 if(!vis[k])
40                 {
41                     vis[k] = 1;
42                     pru.push(k);
43                 }
44             }
45         }
46     }
47     return;
48 }
49 int main()
50 {
51     memset(f, INF, sizeof(f));//初始化 
52     scanf("%d %d", &n, &m);
53     for(register int i = 1; i <= m; ++i)
54     {
55         scanf("%d %d %lld", &x, &y, &z);
56         add(x, y, z);//无向图 
57         add(y, x, z);
58         f[x][y] = f[y][x] = min(f[x][y], z);//取最小(不知道有没有毒瘤数据) 
59     }
60     spfa(1);//从1号节点出发 
61     ans = 1;//初始化为1 
62     for(register int i = 2; i <= n; ++i)//1号节点不算 
63     {
64         int sum = 0;//当前节点的方案数 
65         for(register int j = 1; j <= n; ++j)//枚举 
66         {
67             if(dis[i] == dis[j] + f[i][j])//不解释 
68             {
69                 ++sum;
70             }
71         }
72         if(sum)//这里其实不需要特判,因为每个点都一定会有走最短路这种情况 
73         {
74             ans = ans * sum % MOD;//乘法原理 + 边乘边膜 
75         }
76     }
77     printf("%lld", ans);
78     return 0;
79 }
原文地址:https://www.cnblogs.com/qqq1112/p/11117346.html