题解 P1144 【最短路计数】
看到题解区的很多大佬用的是分层建图,然后跑一边最短路就OK了
然而蒟蒻却想不到这种做法....
首先,看到题目,我们要先想到——暴力!
暴力无疑是很好写的了,用SPFA/Dijkstra/BFS 预处理跑一边最短路,然后爆搜记录最 短路条数。
写了大概十分钟,交了上去(还挣扎一下,开个氧气。
代码如下:
#include <bits/stdc++.h>
#define inf 0x7fffffff
using namespace std;
const int maxn = 2000005;
const int mod = 100003;
int ans[maxn];
int n,m,head[maxn],cnt,dis[maxn],dp[maxn];
bool inq[maxn],vis[maxn];
struct edge{
int to,nxt;
}e[maxn];
void add(int u,int v)
{
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
void SPFA()
{
priority_queue <pair<int,int> > q;
for(int i=2 ;i<=n;++i) dis[i] = inf;
q.push(make_pair(0,1));
inq[1] = 1;
while(!q.empty())
{
int now = q.top().second;
q.pop();
inq[now] = 0;
for(int i = head[now];i;i = e[i].nxt)
{
int to = e[i].to;
if(dis[to] > dis[now] + 1)
{
dis[to] = dis[now] + 1;
q.push(make_pair(-dis[to],to));
inq[to] = 1;
}
}
}
//for(int i=1;i<=n;++i) printf("dis[%d] is %d\n",i,dis[i]);
}
void DFS(int now,int sum)
{
ans[now] ++;
for(int i=head[now];i;i=e[i].nxt)
{
int to = e[i].to;
if(sum + 1 == dis[to])
DFS(to,sum + 1);
}
}
int main ()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
SPFA();
DFS(1,0);
for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
return 0;
}
我们思考优化。
这份代码的瓶颈在DFS部分,导致复杂度到了
其中
很显然了,我们要对DFS进行优化。
基于图论的DFS,我们可以通过 记忆化搜索 或者 DP 进行优化。
我们这里选择使用DP。
考虑一种DP。
1.最优子结构
从图的层次来说,每个节点只需要进行一次遍历出边的松弛操作,就可以找到所有最短路的条数。
可以用反证法证明,如果需要一个节点入队多次,后面的节点深度一定是大于等于前面节点的深度,则一定会更劣,也就是说没有入队的必要。
由此证得。
2.无后效性
如果当前便利的节点为
则就让 B 的 最短路条数 += A 的最短路条数, 且与A的最短路从何而来无关。
有了这些,代码就很好实现了。
注意函数
void DP()
完整代码如下:
#include <bits/stdc++.h>
#define inf 0x7fffffff
using namespace std;
const int maxn = 2000005;
const int mod = 100003;
int n,m,head[maxn],cnt,dis[maxn],dp[maxn];
bool inq[maxn],vis[maxn];
struct edge{
int to,nxt;
}e[maxn];
void add(int u,int v)
{
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
void SPFA()
{
priority_queue <pair<int,int> > q;
for(int i=2 ;i<=n;++i) dis[i] = inf;
q.push(make_pair(0,1));
inq[1] = 1;
while(!q.empty())
{
int now = q.top().second;
q.pop();
inq[now] = 0;
for(int i = head[now];i;i = e[i].nxt)
{
int to = e[i].to;
if(dis[to] > dis[now] + 1)
{
dis[to] = dis[now] + 1;
q.push(make_pair(-dis[to],to));
inq[to] = 1;
}
}
}
//for(int i=1;i<=n;++i) printf("dis[%d] is %d\n",i,dis[i]);
}
void DP()
{
memset(vis,0,sizeof(vis));
queue <int> q;
q.push(1);dp[1]=1;vis[1]=1;dis[1]=0;
while(!q.empty())
{
int now = q.front();q.pop();
for(int i=head[now];i;i=e[i].nxt)
{
int to = e[i].to;
if(dis[to] == dis[now] + 1)
{
dp[to] += dp[now];
dp[to] %= mod;
if(!vis[to])
q.push(to),vis[to] = 1;
}
}
}
}
int main ()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
SPFA();
DP();
for(int i=1;i<=n;++i) printf("%d\n",dp[i]);
return 0;
}