2022/6/30题解报告(以及后续更新)
四个题:天立OI-最短路专题测试
第一题:最短路径
可以使用scanf的特殊bug,对 '-' 的特殊读。
跑一个floyd板子后输出。
#include<cstdio> #include<algorithm> #include<queue> #include<cstring> using namespace std; const int MAXN=100; const long long INF=1e15+10;//最大值 long long f[MAXN][MAXN]; int n,v; int main() { scanf("%d%d",&n,&v); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { long long w; if(scanf("%lld",&w)==1) f[i][j]=w;//scanf对'-'的特殊判断,读入数字就存入f数组 else f[i][j]=INF;//是'-'就赋最大值INF }
//floyd板子 for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(i==j) continue; f[i][j]=min(f[i][j],f[i][k]+f[k][j]); }
//输出 for(int i=1;i<=n;i++) { if(i==v) continue; printf("(%d -> %d) = %lld\n",v,i,f[v][i]); } return 0; }
第二题:分糖果。
分析:
每个小朋友边吃边发糖,所以每个小朋友把糖发给下一个小朋友需要一分钟。
So,我们直接根据数据建立一个边权为1的无向图。
So,分析后直接跑一个单源最短路。
小朋友吃糖需要m分钟
加一个特殊处理printf("%d",ans+m+1);//ans是算出来的最短路
#include<cstdio> #include<algorithm> #include<queue> #include<cstring> using namespace std; const int MAXN=1e4+10,MAXM=1e6+10;//MAXN为最多点数,MAXM为做多边数 int n,p,c,m,ans=0; int head[MAXN],to[MAXM],nxt[MAXM],tot=0,dis[MAXN]; priority_queue<pair <int,int> > q; bool vis[MAXN]; void add(int x,int y)//邻接表建边 { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void scan()//输入 { scanf("%d%d%d%d",&n,&p,&c,&m); for(int i=1;i<=p;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } } void solve()//堆优化djikstra求最短路 { memset(dis,0x3f,sizeof(dis));//初始化 dis[c]=0; q.push(make_pair(0,c)); while(q.size()) { int x=q.top().second;q.pop(); if(vis[x]) continue; vis[x]=1; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(dis[y]>dis[x]+1) { dis[y]=dis[x]+1; q.push(make_pair(-dis[y],y)); } } } } void print()//输入 { for(int i=1;i<=n;i++) { if(dis[i]==0x3f3f3f3f) continue; ans=max(ans,dis[i]); //找最长的最短路 } printf("%d\n",ans+m+1);//输出特殊处理 } int main() { scan(); solve(); print(); return 0;//好习惯 }
第三题:过路费
根据题目描述加上数据范围提示(n<=250),很容易想到本题需要用floyd解决。
可以想到想跑一遍floyd,再加上每一个最短路径中的最大点权。
但也很容易想到这样做出来不一定是最优解。
所以我们来分析一下。
floyd特性:floyd在循环时是不需要考虑各个点的先后顺序的。
为了找出最优解,我们可以先按照点权排序,然后在floyd的第一层循环中按照排序后的点的顺序来访问。
这么做的好处是我们在松弛时先用小的点权小的点松弛总比用点权大的点松弛更有优势
So,直接上代码。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int MAXN=300; int n,m,q; int f[MAXN][MAXN],c[MAXN],d[MAXN][MAXN]; pair<int,int> a[MAXN]; int main() { scanf("%d%d%d",&n,&m,&q); memset(f,0x3f,sizeof(f)); memset(d,0x3f,sizeof(d));//初始化 for(int i=1;i<=n;i++) { int x; scanf("%d",&x); a[i]=make_pair(x,i);//用pair建数组方便排序 c[i]=x; f[i][i]=0; } sort(a+1,a+n+1);//按点权排序 for(int i=1;i<=m;i++) { int x,y,w; scanf("%d%d%d",&x,&y,&w); f[x][y]=f[y][x]=min(f[x][y],w); } for(int t=1;t<=n;t++) { int k=a[t].second;//按点权排序后的点的顺序 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { f[i][j]=min(f[i][j],f[i][k]+f[k][j]); d[i][j]=min(d[i][j],f[i][j]+max(max(c[i],c[j]),c[k]));//松弛 } } for(int i=1;i<=q;i++) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",d[x][y]); } return 0; }
第四题:
可以跑一个单源最短路,然后反向建图,再跑一遍。
我因为脑子抽筋,写了一个O(n^2*logn)的n源djikstra
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; const int MAXN=1e3+10,MAXM=1e5+10; int n,m,k; int head[MAXN],to[MAXM],nxt[MAXM],edge[MAXM],tot; int dis[MAXN][MAXN]; bool vis[MAXN]; void add(int x,int y,int w) { to[++tot]=y; edge[tot]=w; nxt[tot]=head[x]; head[x]=tot; } void scan() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++) { int x,y,w; scanf("%d%d%d",&x,&y,&w); add(x,y,w); } memset(dis,0x3f,sizeof(dis)); } void djikstra(int s) { dis[s][s]=0; memset(vis,0,sizeof(vis)); priority_queue< pair<int,int> > q; q.push(make_pair(0,s)); while(!q.empty()) { int x=q.top().second;q.pop(); if(vis[x]) continue; vis[x]=1; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(dis[s][y]>dis[s][x]+edge[i]) { dis[s][y]=dis[s][x]+edge[i]; q.push(make_pair(-dis[s][y],y)); } } } } void solve() { for(int i=1;i<=n;i++) djikstra(i); } void print() { int ans=0; for(int i=1;i<=n;i++) ans=max(ans,dis[k][i]+dis[i][k]); printf("%d\n",ans); return ; } int main() { scan(); solve(); print(); return 0;
}

浙公网安备 33010602011771号