题型:图论
题意:
有n个点构成的图(有向图),边代表铁轨,电车经过这个点到另一个点时,若铁轨为起始的铁轨方向则不用扳轨道,否则需要扳轨道后才能走,问从a点到b点最少要扳几次轨道,不可到达输出-1。
首先输入n,a,b,下接n行,每一行第一个为Ki,代表从i点出发的铁轨数,然后输入Ki个数,代表到达的点。
分析:
转换一下,对于边<x,y>,假设从x点到y点需要扳一次轨道,那么就可以看成<x,y>的权值为1,若不需要扳轨道,那么权值为0。这样就把问题转换成了求从a到b的单源最短路径问题.
为了扎实基本功,我分别手写Dijkstra,SPFA,Floyd三种算法过了此题。
代码:
Dijkstra实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define MAXN 105
#define INF 0x3f3f3f3f
using namespace std;
int path[MAXN][MAXN];
int dis[MAXN],n,a,b,pre[MAXN];
bool vis[MAXN];
void init(){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
path[i][j]=path[j][i]=INF;
}
}
}
void Dijkstra(int start){
int min,k;
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++){
if(i!=start){
dis[i]=path[start][i];
pre[i]=start;
}
}
dis[start]=0;
vis[start]=true;
for(int i=1;i<=n;i++){
min=INF;
k=0;
for(int j=1;j<=n;j++){
if(!vis[j] && dis[j]<min){
min=dis[j];
k=j;
}
}
if(k==0) return;
vis[k]=true;
for(int j=1;j<=n;j++){
if(!vis[j] && path[k][j]!=INF && dis[j]>dis[k]+path[k][j]){
dis[j]=dis[k]+path[k][j];
pre[j]=k;
}
}
}
}
int main(){
while(~scanf("%d%d%d",&n,&a,&b)){
init();
int num,x;
for(int i=1;i<=n;i++){
scanf("%d",&num);
for(int j=0;j<num;j++){
scanf("%d",&x);
if(j==0) path[i][x]=0;
else path[i][x]=1;
}
}
Dijkstra(a);
if(dis[b]==INF) printf("-1\n");
else printf("%d\n",dis[b]);
}
return 0;
}
SPFA实现
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#define MAXN 123456
#define INF 0x3f3f3f3f
using namespace std;
int n,a,b,nc;
int head[MAXN];
struct Edge{
int to,w,next;
}edge[MAXN];
void add(int i,int j){
edge[nc].to=j;
edge[nc].next=head[i];
head[i]=nc++;
}
int dis[MAXN];
bool vis[MAXN];
bool SPFA(int start){
int k,queue[MAXN],iq,top,outque[MAXN];
for(int i=0;i<=n;i++)
dis[i]=INF;
memset(vis,false,sizeof(vis));
memset(outque,0,sizeof(outque));
iq=0;
queue[iq++]=start;
vis[start]=true;
dis[start]=0;
int i=0;
while(i!=iq){
top=queue[i];
vis[top]=false;
outque[top]++;
if(outque[top]>n) return false;
k=head[top];
while(k>=0){
if(dis[edge[k].to]-edge[k].w>dis[top]){
dis[edge[k].to]=edge[k].w+dis[top];
if(!vis[edge[k].to]){
vis[edge[k].to]=true;
queue[iq]=edge[k].to;
iq++;
}
}
k=edge[k].next;
}
i++;
}
return true;
}
int main(){
while(~scanf("%d%d%d",&n,&a,&b)){
memset(head,-1,sizeof(head));
nc=0;
int num,x;
for(int i=1;i<=n;i++){
scanf("%d",&num);
for(int j=0;j<num;j++){
scanf("%d",&x);
if(j==0) edge[nc].w=0;
else edge[nc].w=1;
add(i,x);
}
}
if(SPFA(a)){
if(dis[b]==INF) printf("-1\n");
else printf("%d\n",dis[b]);
}
else printf("-1\n");
}
return 0;
}
Floyd实现
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#define INF 0x3f3f3f3f
using namespace std;
int path[105][105];
int main() {
int n,a,b;
while(~scanf("%d%d%d",&n,&a,&b)) {
memset(path,INF,sizeof(path));
int num,x;
for(int i=1; i<=n; i++) {
scanf("%d",&num);
for(int j=1; j<=num; j++) {
scanf("%d",&x);
if(j==1)
path[i][x]=0;
else
path[i][x]=1;
}
}
for(int k=1; k<=n; k++) {
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(path[i][k]+path[k][j]<path[i][j])
path[i][j]=path[i][k]+path[k][j];
}
}
}
if(path[a][b]==INF) printf("-1\n");
else printf("%d\n",path[a][b]);
}
return 0;
}