week7作业:Floyd算法、dijkstra、SPFA

A

题意

N个人,M个胜负关系表示为(A,B),胜负关系具有传递性。求出每组无法判断胜负关系的组数(A,B与B,A等价)。

  • 输入
3
3 3
1 2
1 3
2 3
3 2
1 2
2 3
4 2
1 2
3 4
  • 输出
0
0
4

思路

  • 题目类型定位:图,找路径。
  • 运用的算法:Floyd算法的模板
  • 思路:Floyd算法求最短路的部分改为,通过k,ij是否能判断,或者ij直接能判断,代码描述为:
    dis[i][j]=max(dis[i][j],dis[i][k]&dis[k][j]);

总结

  • 直接Floyd会TLE,需要剪枝预判:
    若dis[i][k]==0,则dis[i][k]&dis[k][j]一定为0,不需要再次循环判断
  • Floyd模板:
    在这里插入图片描述

代码

#include<cstdio>
#include<algorithm>
using namespace std;

const int MAXN=505;
int dis[MAXN][MAXN];
int N,M;

int main(void){
    int group;
    scanf("%d",&group);
    while(group--){
        scanf("%d%d",&N,&M);
        for(int x=1;x<=N;x++)
            for(int y=1;y<=N;y++)
                dis[x][y]=0;

        for(int j=0;j<M;j++){
            int A,B;
            scanf("%d%d",&A,&B);
            dis[A][B]=1;
        }
        
        for(int k=1;k<=N;k++){
            for(int i=1;i<=N;i++){
                if(dis[i][k]==0) continue;
                for(int j=1;j<=N;j++){
                    dis[i][j]=max(dis[i][j],dis[i][k]&dis[k][j]);
                }
            }
        }

        int res=0;
        for(int i=1;i<=N;i++){
            for(int j=i+1;j<=N;j++){
                if(dis[i][j]==0&&dis[j][i]==0)
                    res++;
            }
        }
        printf("%d\n",res);
    }
    return 0;
}

B

题意

现可乘坐经济线和商业线,票价速度均不同。现可以乘坐1次商业线,忽略换乘时间,求到机场最快的线路。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

  • 输入
4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3
  • 输出
1 2 4
2
5

思路

  • 问题定位:图,最短路问题
  • 问题算法:无负边的单源最短路 ——dijkstra
  • 思路:
    -1. 存图:前向星,2个点存2条边(无向图)
    -2. 分别从起点、终点运用dijkstra,并记录到各点的最短路结果,并设置当前最短路为不经商业线到达终点
    -3. 枚举每一条商业线。将到达商业线两端的点,利用上述最短路结果连接商业线,求出与当前最短路的最小值,更新最短路结果

总结

dijkstra模板(前向星描述图):
在这里插入图片描述

代码

#include<cstdio>
#include<queue>
using namespace std;

const int MAXN=1e5;
const int INF=1e9;

int cases;
int N,S,E,M,K;
struct Edge{
    int to,w,next;
}edges_ec[2*MAXN];
int head_ec[MAXN],tot_ec;
int dis1[MAXN],dis2[MAXN],vis[MAXN];

int pre1[MAXN],pre2[MAXN];

void init_all(){
    for(int i=0;i<=N;i++)
        head_ec[i]=-1;
    tot_ec=0;
}
void init_ec(int from,int to,int w){
    edges_ec[++tot_ec].to=to;
    edges_ec[tot_ec].w=w;
    edges_ec[tot_ec].next=head_ec[from];
    head_ec[from]=tot_ec;
}
void display(int *pre, int i){
    if(i==S){
        printf("%d",i);
        return;
    }
    display(pre,pre[i]);
    printf(" %d",i);
}
void display2(int i){
    if(i==E){
        printf(" %d",i);
        return;
    }
    printf(" %d",i);
    display2(pre2[i]);
}

void dijkstra(int s,int *dis,int *pre){
    priority_queue<pair<int,int> > q;
    for(int i=0;i<=N;i++){
        vis[i]=0;
        dis[i]=INF;
        pre[i]=0;
    }
    dis[s]=0;

    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_ec[x]; i!=-1; i=edges_ec[i].next){
            int y=edges_ec[i].to,w=edges_ec[i].w;
            if(dis[y]>dis[x]+w){
                dis[y]=dis[x]+w;
                pre[y]=x;
                q.push(make_pair(-dis[y],y));
            }
        }
    }
}



int main(void){
    while(~scanf("%d%d%d",&N,&S,&E)){
        init_all();
        scanf("%d",&M);
        for(int i=0;i<M;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            init_ec(x,y,z);
            init_ec(y,x,z);
        }
        dijkstra(S,dis1,pre1);
        dijkstra(E,dis2,pre2);

        int uBU=0,vBU=0;
        int minLength=dis1[E];
        int flag=0;
        scanf("%d",&K);
        for(int i=0;i<K;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            if(dis1[v]+dis2[u]+w<minLength){
                minLength=dis1[v]+dis2[u]+w;
                uBU=v,vBU=u;
                flag=1;
            }
            if(dis1[u]+dis2[v]+w<minLength){
                minLength=dis1[u]+dis2[v]+w;
                uBU=u,vBU=v;
                flag=1;
            }
        }

        if(cases!=0) printf("\n");
        cases++;

        if(flag==1){
            display(pre1,uBU);
            display2(vBU);
            printf("\n%d\n%d\n",uBU,minLength);
        }
        else{
            display(pre1,E);
            printf("\nTicket Not Used\n%d\n",minLength);
        }
    }
    return 0;
}

C

题意

有 N 个商业城市,编号 1 ~ N,其中 1 号城市是首都。现共有 M 条有向道路供商业城市相互往来。
每一个商业城市有正整数表示的繁荣程度,当沿道路从一个商业城市走到另一个商业城市时,都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。
现想知道从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请打出 ‘?’。

  • 输入
2
5
6 7 8 9 10
6
1 2
2 3
3 4
1 5
5 4
4 5
2
4
5
10
1 2 4 4 5 6 7 8 9 10
10
1 2
2 3
3 1
1 4
4 5
5 6
6 7
7 8
8 9
9 10
2
3 10
  • 输出
Case 1:
3
4
Case 2:
?
?

思路

  • 题目定位:图,求最短路
  • 算法:可能有负环 ——SPFA
  • 思路:运用spfa求最短路,找到负环时,用dfs将负环上的点可达的所有点标记为-INF

总结

spfa模板(图的邻接表描述):
在这里插入图片描述

代码

#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;

const int MAXN=1e5+5;
const int INF=1e9;
int N,M,a[MAXN];
struct Edge{
    int to,w,next;
} edges[MAXN];
int head[MAXN],tot;

void init(){
    for(int i=0;i<=N;i++) head[i]=-1;
    tot=0;
}
void addEdge(int from,int to,int w){
    edges[++tot].to=to;
    edges[tot].w=w;
    edges[tot].next=head[from];
    head[from]=tot;
}

int vis[MAXN],dis[MAXN],cnt[MAXN];
int vis_dfs[MAXN];
void dfs(int p){
    for(int i=head[p]; i; i=edges[i].next){
        int v=edges[i].to;
        if(!vis_dfs[v]){
            vis_dfs[v]=1;
            dis[v]=-INF;
            dfs(v);
        }
    }

}
void spfa(int s){
    queue<int> q;
    for(int i=0;i<=N;i++){
        vis[i]=0;
        dis[i]=INF;
        cnt[i]=0;
    }
    vis[s]=1,dis[s]=0;
    q.push(s);

    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;

        for(int i=head[u]; i; i=edges[i].next){
            int v=edges[i].to,w=edges[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                cnt[v]=cnt[u]+1;
                if(cnt[v]>=N){
                    dis[v]=-INF;
                    dfs(v);
                }
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }

}



int main(void){
    int T;
    scanf("%d",&T);
    for(int t=1;t<=T;t++){
        init();

        scanf("%d",&N);
        for(int i=1;i<=N;i++)
            scanf("%d",&a[i]);
        scanf("%d",&M);
        for(int i=0;i<M;i++){
            int A,B;
            scanf("%d%d",&A,&B);
            addEdge(A,B,pow(a[B]-a[A],3));
        }
        spfa(1);
        printf("Case %d:\n",t);
        int Q;
        scanf("%d",&Q);
        while(Q--){
            int P;
            scanf("%d",&P);
            if(dis[P]<3||dis[P]==INF||dis[P]==-INF)
                printf("?\n");
            else
                printf("%d\n",dis[P]);
        }
    }
    return 0;
}

疑惑:前向星的遍历判断,为什么此处用 i,而不是 i != -1(i != -1 死循环。上题dijkstra用 i 遇到多余的0)
注:
人间迷惑大赏之要输出 Case i: …
结论:测试样例果然是用来测试输入输出格式的…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值