先来弱化版。
这道题数据范围给的不是很大,所以可以开邻接矩阵试试。
提示:两个点之间可能有多条边。要是有两个同向的边我能选更长的?
MLE代码(Floyd+邻接矩阵)(20分)
#include <bits/stdc++.h>
using namespace std;
int e[10001][10001];
int n,m,s,u,v,w;
inline void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(e[i][j]>e[i][k]+e[k][j]){
e[i][j]=e[i][k]+e[k][j];
}
}
}
}
}
inline void scan(){
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
if(i!=j)e[i][j]=INT_MAX/2;
}
}
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
e[u][v]=min(w,e[u][v]);
}
}
int main(){
cin.tie(nullptr),cout.tie(nullptr);
ios::sync_with_stdio(false);
scan();
floyd();
for(int i=1;i<=n;i++){
if(e[s][i]!=INT_MAX/2)cout<<e[s][i]<<" ";
else cout<<INT_MAX<<" ";
}
return 0;
}
Floyd算法传送门:https://www.cnblogs.com/11sgXL/p/13644680.html
MLE原因很简单,算了一下内存,1亿的数组爆炸了,题目限制125MB,数组占了381.47MB,这样都给了20分(感叹)。
用邻接表或者向前星来实现Floyd就会变得很麻烦,要先遍历一遍,所以这道题PASS掉Floyd和邻接矩阵
那么接下来试试常规套餐,邻接表+Dijkstra算法。
AC代码(邻接表+Dijkstra算法)
#include <bits/stdc++.h>
using namespace std;
int n,m,s,dist[10001];
vector<pair<int,int>>e[10001];//距离->编号
bitset<10001>vis;
inline void scan(){
cin>>n>>m>>s;
int u,v,w;
for(int i=0;i<m;i++){
cin>>u>>v>>w;
e[u].push_back({w,v});
}
}
//定义dist[i]为i节点到s节点的最短距离
inline void dijkstra(){
//初始化
for(int i=1;i<=n;i++)
dist[i]=INT_MAX/2;
dist[s]=0;//自己到自己为0
//贪心
for(int i=1;i<=n;i++){
int idx=-1;
//在dist中找最短距离
for(int j=1;j<=n;j++){
//任意第一个
if(idx==-1&&!vis[j])idx=j;
//更小的
if(idx!=-1&&!vis[j]&&dist[j]<dist[idx])idx=j;
}
vis[idx]=true;
//更新和idx相关的dist
for(auto j:e[idx]){
dist[j.second]=min(dist[j.second],j.first+dist[idx]);
}
}
}
int main(){
cin.tie(nullptr),cout.tie(nullptr);
ios::sync_with_stdio(false);
scan();
dijkstra();
for(int i=1;i<=n;i++)
if(dist[i]==INT_MAX/2)cout<<INT_MAX<<" ";
else cout<<dist[i]<<" ";
return 0;
}
果然是弱化版啊(感叹),里面用INT_MAX/2而不用INT_MAX是为了防止某个数加上dist会导致溢出出现负数。
拿弱化版的代码去跑一下标准版试试(喜),Dijkstra算法甚至都还没用上配套的堆优化,因为选点是O(n),但是我们一直都在选最小的那一个head,也就是说可以用小根堆来优化成O(logn)。
标准版
什么都没看直接提交喜提RE。
一看数组范围多扩大了十倍,没有优先队列优化多半要TLE几个点,所以我选择直接吸氧。
然后喜提6个TLE
O2TLE代码(邻接表+Dijkstra算法)照抄弱化版原文
#include <bits/stdc++.h>
using namespace std;
int n,m,s,dist[100001];
vector<pair<int,int>>e[100001];//距离->编号
bitset<100001>vis;
inline void scan(){
cin>>n>>m>>s;
int u,v,w;
for(int i=0;i<m;i++){
cin>>u>>v>>w;
e[u].push_back({w,v});
}
}
//定义dist[i]为i节点到s节点的最短距离
inline void dijkstra(){
//初始化
for(int i=1;i<=n;i++)
dist[i]=INT_MAX/2;
dist[s]=0;//自己到自己为0
//贪心
for(int i=1;i<=n;i++){
int idx=-1;
//在dist中找最短距离
for(int j=1;j<=n;j++){
//任意第一个
if(idx==-1&&!vis[j])idx=j;
//更小的
if(idx!=-1&&!vis[j]&&dist[j]<dist[idx])idx=j;
}
vis[idx]=true;
//更新和idx相关的dist
for(auto j:e[idx]){
dist[j.second]=min(dist[j.second],j.first+dist[idx]);
}
}
}
int main(){
cin.tie(nullptr),cout.tie(nullptr);
ios::sync_with_stdio(false);
scan();
dijkstra();
for(int i=1;i<=n;i++)
if(dist[i]==INT_MAX/2)cout<<INT_MAX<<" ";
else cout<<dist[i]<<" ";
return 0;
}
既然如此那也只能用优先队列来优化选点了。
这么强悍的时间我估计不吸氧气都能过= =
O2AC代码(邻接表+Dijkstra算法+优先队列)
#include <bits/stdc++.h>
using namespace std;
//小根堆
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q;
int n,m,s,dist[100001];
vector<pair<int,int>>e[100001];//距离->编号
bitset<100001>vis;
inline void scan(){
cin>>n>>m>>s;
int u,v,w;
for(int i=0;i<m;i++){
cin>>u>>v>>w;
e[u].push_back({w,v});
}
}
//定义dist[i]为i节点到s节点的最短距离
inline void dijkstra(){
//初始化
for(int i=1;i<=n;i++)
dist[i]=INT_MAX/2;
dist[s]=0;//自己到自己为0
//优先队列选点
q.push({0,s});
while(!q.empty()){
int now=q.top().second;
int nowDis=q.top().first;
q.pop();//要在新节点加入前就pop
if(vis[now])continue;
vis[now]=true;
for(auto i:e[now]){
int toNextLength=i.first;
int nxt=i.second;
if(dist[nxt]>dist[now]+toNextLength){
dist[nxt]=dist[now]+toNextLength;
if(!vis[nxt]){
q.push({dist[nxt],nxt});
}
}
}
}
}
int main(){
cin.tie(nullptr),cout.tie(nullptr);
ios::sync_with_stdio(false);
scan();
dijkstra();
for(int i=1;i<=n;i++)
if(dist[i]==INT_MAX/2)cout<<INT_MAX<<" ";
else cout<<dist[i]<<" ";
return 0;
}
几道和模板题差不多的题:采购特价商品 - 洛谷,Mzc和体委的争夺战 - 洛谷
不过要注意的是用优先队列选点的时候如果vis[now]=true的时候就不用再去找他了,否则喜提TLE。
稍微难了一点的题:[USACO07FEB] Cow Party S - 洛谷
不过也就是正向dj一次加上反向dj一次。
类似的题目有很多,可以点算法标签最短路找,代码就不帖了。
以及转变思维的最小花费 - 洛谷