提示:如果你进来是为了学习dikstra算法的,抱歉你走错片场了
这是一个小白的解题心路分享
解题历程:
刚刚看到这题时,先用了Floyd算法,也就是基于动态规划用dis[u][v]表示城市u到城市v的最短时间,代码如下:
#include<iostream>
#include<cstring>
#define endl "\n"
using namespace std;
typedef long long ll;
constexpr int maxn = 1005;
constexpr ll inf = 0x3f3f3f3f3f3f3f3fL;
ll dis[maxn][maxn];
int main(){
memset(dis,0x3f,sizeof(dis));
int n,m,t;
cin >> n >> m;
for(int i = 1;i<=n;i++)
dis[i][i] = 0;//自己到自己的距离为0
for(int i = 1;i<=m;i++){
int u,v;
ll w;
cin >> u >> v >> w;
dis[u][v] = w;
}
for(int k = 1;k<=n;k++)
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
ll ans = 0;
for(int i = 2;i<=n;i++)
ans += dis[1][i] + dis[i][1];
cout << ans << endl;
return 0;
}
但是没注意时间复杂度,没过qwq
T了能理解,咱就是说为什么前四个点会WA呢?
然后猜测输入会有重边,把dis[u][v] = w 改成 dis[u][v] = min(dis[u][v],w)交了一遍:
果然输入卡重边(这里含泪建议各位写Floyd的时候要不要贪小便宜qwq)
当然对于这个题目来说,这个算法很明显不行,这时就得换算法了
由于最近刚刚学了dijktra,于是打算牵出来遛一遛(bushi
起点为1,终点为2->n,跑一遍就行,然后把所有的值加起来
但是起点为2->n,终点为1时怎么办呢?那就跑n-1遍dijkstra呗(小白思路,各位大佬轻点骂qwq
然后代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define endl "\n"
using namespace std;
typedef long long ll;
constexpr int maxn = 2550;
constexpr ll inf = 0x3f3f3f3f3f3f3f3fL;
struct edge{
int v;
ll w;
bool operator<(const edge& a)const{return w>a.w;}//定义排序方式
}e;
vector <edge> mp[maxn];
int dis[maxn];
void dij(int u){
bool vis[maxn] = { false };//区别S点集和T点集,当vis[u] = true认为u在S点集中
memset(dis,0x3f,sizeof(dis));//初始化dis
dis[u] = 0;
priority_queue<edge> T;//这里并不是存边,这里存的时当前状态下某一点和他的dis,偷懒下
T.push({u,0});//源点及其距离推入队列
while(!T.empty()){
u = T.top().v;//取出队列中当前dis最小的值
T.pop();
if(vis[u])//如果已经访问过就跳过
continue;
vis[u] = true;//将其加入S集
// for(auto&[v,w]:mp[u]){
int len = mp[u].size();//松弛其所有出边,并将其终点加入队列
for(int i = 0;i<len;i++){
int v = mp[u][i].v;
ll w = mp[u][i].w;
if(dis[v]>dis[u]+w){
dis[v] = dis[u] + w;
T.push({v,dis[v]});
}
}
}
}
int main(){
int m,n,s,t;
cin >> n >> m;
for(int i = 0;i<m;i++){
int v,u;
ll w;
cin >> u >> v >> w;
e.v = v;
e.w = w;
mp[u].emplace_back(e);
// e.v = u;
// mp[v].emplace_back(e);
}
ll ans = 0;
dij(1);
for(int i = 2;i<=n;i++)
ans += dis[i];
for(int i = 2;i<=n;i++){
dij(i);
ans += dis[1];
}
cout << ans << endl;
return 0;
}
然后又成功的T了
然后突然我脑袋里冒出了一个大胆的想法:反向dijkstra建图!
既然正向的可以解决1到2->n的时间,反向的话是不是就能解决2->n到1了
然后相当于只用跑2遍dijkstra就行,有思路就好搞了对吧
AC代码如下:
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define endl "\n"
using namespace std;
typedef long long ll;
constexpr int maxn = 1005;
constexpr ll inf = 0x3f3f3f3f3f3f3f3fL;
struct edge{
int v;
ll w;
bool operator<(const edge& a)const{return w>a.w;}//定义排序方式
}e;
struct edge2{
int u;
ll w;
bool operator<(const edge& a)const{return w>a.w;}
}e2;//但是后来想了下好像这里没必要再定义个结构体,读者有兴趣可以试试减少代码量
vector <edge> mp[maxn];
vector <edge2> mp2[maxn];
int dis[maxn] = { 0 };
void dij(int u){
bool vis[maxn] = { false };//区别S点集和T点集,当vis[u] = true认为u在S点集中
memset(dis,0x3f,sizeof(dis));//初始化dis
dis[u] = 0;
priority_queue<edge> T;//这里并不是存边,这里存的时当前状态下某一点和他的dis
T.push({u,0});//源点及其距离推入队列
while(!T.empty()){
u = T.top().v;//取出队列中当前dis最小的值
T.pop();
if(vis[u])//如果已经访问过就跳过
continue;
vis[u] = true;//将其加入S集
// for(auto&[v,w]:mp[u]){
int len = mp[u].size();//松弛其所有出边,并将其终点加入队列
for(int i = 0;i<len;i++){
int v = mp[u][i].v;
ll w = mp[u][i].w;
if(dis[v]>dis[u]+w){
dis[v] = dis[u] + w;
T.push({v,dis[v]});
}
}
}
}
void dij2(int v){
bool vis[maxn] = { false };//咱就是说复制下上面的就行了对吧,u,v互换下
memset(dis,0x3f,sizeof(dis));
dis[v] = 0;
priority_queue<edge> T;
T.push({v,0});
while(!T.empty()){
v = T.top().v;
T.pop();
if(vis[v])
continue;
vis[v] = true;
int len = mp2[v].size();
for(int i = 0;i<len;i++){
int u = mp2[v][i].u;
ll w = mp2[v][i].w;
if(dis[u]>dis[v]+w){
dis[u] = dis[v] + w;
T.push({u,dis[u]});
}
}
}
}
int main(){
int m,n,s,t;
cin >> n >> m;
for(int i = 0;i<m;i++){
int v,u;
ll w;
cin >> u >> v >> w;
e.v = v;
e2.u = u;
e.w = w;
e2.w = w;
mp[u].emplace_back(e);
mp2[v].emplace_back(e2);
}
ll ans = 0;
dij(1);
for(int i = 2;i<=n;i++)
ans += dis[i];
dij2(1);
for(int i = 2;i<=n;i++){
ans += dis[i];
}
cout << ans << endl;
return 0;
}
可能看着臭长了些,但能AC的代码都是好代码对吧【doge
好耶!!!
说明,写本文前搜了下发现已经有很多人用这个思路过了,但想了下我还是写了出来
因为不管怎么说这也是我这个小菜狗独立完成的不是【doge
好激动,就当是一次分享心情吧 ,各位看官笑笑就行,不用赞之类的
希望以后每次解决题目也会像今天这般欣喜
好了废话不多说,如有不对之处还请各位雅正(轻点骂,我怕疼bushi
祝各位天天AC好心情