生活随笔
收集整理的這篇文章主要介紹了
最短路径生成树计数+最短路径生成树
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最短路徑生成樹計數。
我們應該先明白什么是最短路徑生成樹,不會戳這里。
計數方法明顯是要使用乘法原理計數,也就是說我們可以得出每一步的方案數再乘進答案中。
接下來考慮如何的出每一步的方案數,所謂方案數也就是對于每一個D[i]D[i]D[i]可以從多少個D[j]D[j]D[j]轉移過來,那么顯然,比較大的距離只能從比較小的距離轉移過來。
所以我們可以先給DDD數組遞增排序。 這樣比iii小的都是有可能轉移到iii的點,那么我們只需要判斷一下他們之間有沒有邊可以轉移到即可。放一個圖可能大家好理解。
????
只要滿足源點到達任意點的距離的權值最小的樹就是最短路徑生成樹,也就是說不唯一。下面代碼是非優化版。
#include<iostream>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std
;
#define read(x) scanf("%lld",&x)
#define Read(x,y) scanf("%lld%lld",&x,&y)
#define gc(x) scanf(" %c",&x);
#define mmt(x,y) memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod ((1LL<<31) - 1LL)
const ll N
= 1005;
const ll M
= 1e6;
ll d
[N
];
bool vis
[N
];ll id
[N
];
ll head
[N
],tot
;
ll w
[N
][N
];
struct Edge
{ll next
;ll to
;ll dis
;
}edge
[M
*2];
inline void add(ll from
,ll to
,ll dis
)
{edge
[++tot
].next
= head
[from
];edge
[tot
].to
= to
;edge
[tot
].dis
= dis
;head
[from
] = tot
;
}
void spfa()
{mmt(d
,0x3f);mmt(vis
,0);d
[1] = 0;queue
<ll
> Q
;Q
.push(1);vis
[1] = 1;while(Q
.size()){ll x
= Q
.front();Q
.pop();vis
[x
] = 0;for(ll i
= head
[x
];~i
;i
= edge
[i
].next
){ll y
= edge
[i
].to
;ll dis
= edge
[i
].dis
;if(d
[y
] > d
[x
] + dis
){d
[y
] = d
[x
]+dis
;if(!vis
[y
]){vis
[y
] = 1;Q
.push(y
);}}}}
}
bool cmp(ll a
,ll b
)
{return d
[a
] < d
[b
];
}
void init()
{mmt(head
,-1);tot
= 0;for(int i
= 1;i
<=1000;++i
){for(int j
= 1;j
<= 1000;++j
){w
[i
][j
] = INF
;}w
[i
][i
] = 0;}
}
int main()
{init();ll n
,m
;ll f
,t
,dis
;Read(n
,m
);for(ll i
= 1;i
<= m
;++i
){Read(f
,t
);read(dis
);if(w
[f
][t
] > dis
) w
[f
][t
] = w
[t
][f
] = dis
;add(f
,t
,dis
);add(t
,f
,dis
);}spfa();for(ll i
= 1;i
<= n
;++i
) id
[i
] = i
;sort(id
+1 ,id
+ n
+1,cmp
);ll ans
= 1;ll cnt
= 0;for(ll i
= 2;i
<= n
;++i
){cnt
= 0;for(ll j
= 1;j
<= i
-1;++j
){if(d
[id
[i
]] == d
[id
[j
]] + w
[id
[j
]][id
[i
]]) cnt
++;}ans
= ans
* cnt
%mod
;}cout
<<ans
<<endl
;}
最短路徑生樹,邊權最小生成樹的距離和
也就是說,對于一個邊,盡可能的使到達他的邊經過松弛,還是上圖:
我們這里的話我們應該選了2 3因為這樣邊權和最小。所以我們接助一個數組來表示到達該點的距離,每次松弛該點時記錄最小值,求和即可。
#include<iostream>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std
;
#define read(x) scanf("%lld",&x)
#define Read(x,y) scanf("%lld%lld",&x,&y)
#define gc(x) scanf(" %c",&x)
#define mmt(x,y) memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod ((1LL<<31) - 1LL)
const ll N
= 3e5+5;
const ll M
= 1e6;
ll d
[N
];
bool vis
[N
];
ll head
[N
],tot
;
ll p
[N
];
struct Edge
{ll next
;ll to
;ll dis
;
}edge
[N
*2];
inline void add(ll from
,ll to
,ll dis
)
{edge
[++tot
].next
= head
[from
];edge
[tot
].to
= to
;edge
[tot
].dis
= dis
;head
[from
] = tot
;
}
struct node
{ll id
,val
;node(){}node(ll a
,ll b
):id(a
),val(b
){}bool operator <(node A
)const{return val
> A
.val
;}
};
void dij(ll u
)
{mmt(p
,0x7f);mmt(d
,0x7f);mmt(vis
,0);d
[u
] = 0;p
[u
] = 0;priority_queue
<node
> Q
;Q
.push({u
,0});node tmp
;while(Q
.size()){tmp
= Q
.top();Q
.pop();int x
= tmp
.id
;if(vis
[x
]) continue;vis
[x
] = 1;for(int i
= head
[x
];~i
;i
= edge
[i
].next
){int y
= edge
[i
].to
;ll dis
= edge
[i
].dis
;if(d
[y
] >= d
[x
] + dis
){p
[y
] = min(p
[y
],dis
);d
[y
] = d
[x
] +dis
;Q
.push({y
,d
[y
]});}}}
}
void init()
{mmt(head
,-1);tot
= 0;
}
int main()
{init();ll n
,m
;ll f
,t
,dis
;Read(n
,m
);for(ll i
= 1;i
<= m
;++i
){Read(f
,t
);read(dis
);add(f
,t
,dis
);add(t
,f
,dis
);}read(t
);dij(t
);ll ans
= 0;for(int i
= 1;i
<= n
;++i
){ans
+= p
[i
];}cout
<<ans
<<endl
;
}
網上最短路徑生成樹大都是矩陣N^2枚舉,如果3000點以上那必然會超時。我們換換思想,如果在Djstra出隊時只要他更新的權值等于最短路徑那么將成為cnt數組之一,也就是說我們不必要N ^2枚舉,只要再做一遍Dikjstra就可以了。因為網上沒有找到相似的思路,所以程序有很多可以詬病的地方,希望大家指正。
#include<iostream>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std
;
#define read(x) scanf("%lld",&x)
#define Read(x,y) scanf("%lld%lld",&x,&y)
#define gc(x) scanf(" %c",&x)
#define mmt(x,y) memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod ((1LL<<31) - 1LL)
const ll N
= 1005;
const ll M
= 1e6;
ll d
[N
];
bool vis
[N
];
ll head
[N
],tot
;
ll p
[N
];
struct Edge
{ll next
;ll to
;ll dis
;
}edge
[M
*2];
inline void add(ll from
,ll to
,ll dis
)
{edge
[++tot
].next
= head
[from
];edge
[tot
].to
= to
;edge
[tot
].dis
= dis
;head
[from
] = tot
;
}
struct node
{ll id
,val
;node(){}node(ll a
,ll b
):id(a
),val(b
){}bool operator <(node A
)const{return val
> A
.val
;}
};
void dij(ll u
,ll id
)
{mmt(p
,0);if(id
== 0) mmt(d
,0x7f); mmt(vis
,0);d
[u
] = 0;p
[u
] = 1;priority_queue
<node
> Q
;Q
.push({u
,0});node tmp
;while(Q
.size()){tmp
= Q
.top();Q
.pop();int x
= tmp
.id
;if(vis
[x
]) continue;vis
[x
] = 1;for(int i
= head
[x
];~i
;i
= edge
[i
].next
){int y
= edge
[i
].to
;ll dis
= edge
[i
].dis
;if(d
[y
] >= d
[x
] + dis
){if(id
== 1) p
[y
] ++;d
[y
] = d
[x
] +dis
;Q
.push({y
,d
[y
]});}}}
}
void init()
{mmt(head
,-1);tot
= 0;
}
int main()
{init();ll n
,m
;ll f
,t
,dis
;Read(n
,m
);for(ll i
= 1;i
<= m
;++i
){Read(f
,t
);read(dis
);add(f
,t
,dis
);add(t
,f
,dis
);}dij(1,0);dij(1,1);ll ans
= 1;for(int i
=1;i
<= n
;++i
){ans
= ans
*p
[i
]%mod
;}cout
<<ans
;}
總結
以上是生活随笔為你收集整理的最短路径生成树计数+最短路径生成树的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。