题意:求每头奶牛来回最短路之和最大是多少。
思路:
因为要求的最短路条数有很多为了一劳永逸(懒),所以首先想到floyd。
但最后TLE了。
题目链接
附代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
#define MMM 0x3f3f3f3f
int cnt[1005][1005]; int n,m,x;//floyd解法
void flyod(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cnt[i][j]=min(cnt[i][j],cnt[i][k]+cnt[k][j]);
}
}
}
}
signed main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>x;
memset(cnt,MMM,sizeof(cnt));
for(int i=0;i<m;i++){
int a,b,c; cin>>a>>b>>c;
cnt[a][b]=min(cnt[a][b],c);
}
flyod();
int ans;
for(int i=1;i<=n;i++){
int k;
if(i==x) k=0;
if(i!=x) k=cnt[i][x]+cnt[x][i];
if(i==1){
ans=k;
}
else ans=max(k,ans);
}
cout<<ans<<endl;
return 0;
}
所以又采用了朴素dijkstra每头奶牛来回跑两次dijkstra就是。
果然还是TLE了。
附代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
#define MMM 0x3f3f3f3f
int cnt[1005][1005]; int dis[1005]; int vis[1005]; int n,m,x;
int dij(int t,int c){
memset(vis,0,sizeof(vis));
vis[t]=1;
for(int i=1;i<=n;i++){
dis[i]=cnt[t][i];
}
for(int i=1;i<n;i++){
int MM=MMM,k=0;
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]<MM){
MM=dis[j]; k=j;
}
}
vis[k]=1;
for(int j=1;j<=n;j++){
if(!vis[j]&&(dis[k]+cnt[k][j])<dis[j]){
dis[j]=dis[k]+cnt[k][j];
}
}
}
return dis[c];
}
signed main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>x;
memset(cnt,MMM,sizeof(cnt));
for(int i=0;i<m;i++){
int a,b,c; cin>>a>>b>>c;
cnt[a][b]=c;
}
int ans;
for(int i=1;i<=n;i++){
int k;
if(i==x) k=0;
if(i!=x){
k=dij(i,x)+dij(x,i);
}
if(i==1) ans=k;
else ans=max(ans,k);
}
cout<<ans<<endl;
}
然后束手无策的我去找题解才发现这是道思维题。
当以终点为起点时其实只跑一遍就可以找到回去所有点的最短路,那来的最短路怎么找呢其实可以将每条边的起点和终点交换,如:1->2->3最短路为(1,3),那我们将边方向改变为1<-2<-3明显此时最短路(3,1)和开始的(1,3)是相等的。依照这个思路来的最短路其实等于每条边反向时的回去的最短路,所以就将方向改变后对终点进行一次dijkstra就行了。
附ac代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
#define MMM 0x3f3f3f3f
int cnt[1005][1005]; int dis[1005]; int vis[1005]; int n,m,x; int dis2[1005]; int dis3[1005];
void dij(int t){
memset(vis,0,sizeof(vis));
vis[t]=1;
for(int i=1;i<=n;i++){
dis[i]=cnt[t][i];
}
for(int i=1;i<n;i++){
int MM=MMM,k=0;
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]<MM){
MM=dis[j]; k=j;
}
}
vis[k]=1;
for(int j=1;j<=n;j++){
if(!vis[j]&&(dis[k]+cnt[k][j])<dis[j]){
dis[j]=dis[k]+cnt[k][j];
}
}
}
return ;
}
void zz(){//矩阵转置(就是把每条边的起点和终点交换)
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
swap(cnt[i][j],cnt[j][i]);
}
}
}
signed main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>x;
memset(cnt,MMM,sizeof(cnt));
for(int i=0;i<m;i++){
int a,b,c; cin>>a>>b>>c;
cnt[a][b]=min(cnt[a][b],c);
}
dij(x);
for(int i=1;i<=n;i++){
if(i==x) continue;
else dis2[i]=dis[i];//以x为起点最短路
}
zz();
dij(x);
for(int i=1;i<=n;i++){
if(i==x) continue;
else dis3[i]=dis[i];//以x为终点最短路
}
int ans;
for(int i=1;i<=n;i++){
int k;
k=dis2[i]+dis3[i];
if(i==1) ans=k;
else ans=max(ans,k);
}
cout<<ans<<endl;
return 0;
}