题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6026
题意:有一个n个节点的无向图,节点标记为0~n-1,边长的范围是[0,9],现在希望删除这个图中的一些边,使得这个图变为一个有n-1条边的树,并且对于此树中的每一个节点v,它到0节点的距离与原图中点v与点0之间的最短路径值相同,用矩阵给出原图中任意两点间边的长度,问有多少种方法得到此树。
解析:先用spfa求出所有点到点0的最短路径,然后枚举每一点求出从0点经过中间点i到点j的路径数in[j],且此路径要与原图中0点到j点的最短路径长度相同,那么总的方案数就是in[1]*in[2]*...in[n-1] , 因为每个点都从in[i]条间接路径中选择一条留下其余删掉方案数就是in[i],总的方案数就是in[i]乘起来。
注意:in[i]也包括了0点和点i之间直接有边相连的情况。
原理大概是,在上面删除过程进行完毕之后,每条边的"前面节点",也就是必须经过前面节点才能到达0节点,这样的话每个点只关系但一条由"前面节点"过来的边,n个节点共n条边,但是0节点没有"前面节点"也就没有对应的边,所以共n-1条边 。
代码:
#include <iostream> #include <cstdio> #include <queue> #include <cstring> using namespace std; typedef long long ll; #define M 55 const int inf=0x3f3f3f3f; const int mod=1e9+7; int n,in[M],dis[M],vis[M],Map[M][M]; char str[M][M]; void init() { memset(in,0,sizeof(in)); memset(Map,0,sizeof(Map)); } void spfa() { memset(vis,0,sizeof(vis)); for(int i=0;i<=n;i++) dis[i]=inf; queue<int>Q; Q.push(0); dis[0]=0; vis[0]=1; while(!Q.empty()) { int u=Q.front(); Q.pop(); vis[u]=0; for(int i=0;i<n;i++) { if(!Map[u][i]) continue; int v=Map[u][i]; if(dis[i]>=dis[u]+v) { dis[i]=dis[u]+v; if(!vis[i]) { vis[i]=1; Q.push(i); } } } } } int main() { while(scanf("%d",&n)!=EOF) { init(); for(int i=0;i<n;i++) { scanf("%s",str[i]); for(int j=0;j<n;j++) { if(i==j) continue; Map[i][j]=Map[j][i]=str[i][j]-'0'; } } spfa(); for(int i=0;i<n;i++) //i是0到j的中间点 { for(int j=0;j<n;j++)//计算in[j] { if(i!=j&&dis[j]==dis[i]+Map[i][j]&&Map[i][j]) in[j]++; } } ll sum=1; for(int i=1;i<n;i++) { sum=(sum*in[i])%mod; } printf("%lld\n",sum); } return 0; }