zoukankan      html  css  js  c++  java
  • Week7 HomeWork

     A-

    众所周知,TT 有一只魔法猫。
    
    这一天,TT 正在专心致志地玩《猫和老鼠》游戏,然而比赛还没开始,聪明的魔法猫便告诉了 TT 比赛的最终结果。TT 非常诧异,不仅诧异于他的小猫咪居然会说话,更诧异于这可爱的小不点为何有如此魔力?
    
    魔法猫告诉 TT,它其实拥有一张游戏胜负表,上面有 N 个人以及 M 个胜负关系,每个胜负关系为 A B,表示 A 能胜过 B,且胜负关系具有传递性。即 A 胜过 B,B 胜过 C,则 A 也能胜过 C。
    
    TT 不相信他的小猫咪什么比赛都能预测,因此他想知道有多少对选手的胜负无法预先得知,你能帮帮他吗?

    Input

    第一行给出数据组数。
    
    每组数据第一行给出 N 和 M(N , M <= 500)。
    
    接下来 M 行,每行给出 A B,表示 A 可以胜过 B。

    Output

    对于每一组数据,判断有多少场比赛的胜负不能预先得知。注意 (a, b) 与 (b, a) 等价,即每一个二元组只被计算一次。

    Sample Input

    3
    3 3
    1 2
    1 3
    2 3
    3 2
    1 2
    2 3
    4 2
    1 2
    3 4

    Sample Output

    0
    0
    4

    思路分析:

      本题考察Floyd-Warshall算法,这个算法是解决图中每两点的距离问题的,我们只要记得如下三重循环即可。

    for(int k=0;k<n;k++){
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
            }
        }
    }

      我们可以得到这样一个关系:如果a>b,b>c,则就有a>c,该关系具有传递性,所以这个题的目的就是让我们求一个传递闭包。所以我们可以将边权换成1来处理,只要表达出传递性即可。因为要考虑图中任意两点的距离问题,所以可以用弗洛伊德算法来求解。

      对于本题我们只要求传递闭包,所以只要将代码改成

        d[i][j]=d[i][j]||(d[i][k]&&d[k][j])
    即可。
      

      

    用处:最短路、求传递闭包。复杂度O(n^3),有时要优化。 

      对于本题,可以进行适当的剪枝操作。我们可以在第三重循环前加一个判断,如果i,k比赛不可知,那么第三重循环中的判断肯定为0,所以无需进行第三重循环,直接跳过即可。只有当i,k比赛可知时,第三重循环才有意义。

    以下是源代码:

    #include<iostream>
    using namespace std;
    int n,m;
    bool d[510][510];
    int cnt;
    
    int main(){
        int c;
        scanf("%d",&c);
        for(int ii=0;ii<c;ii++){
            scanf("%d %d",&n,&m);
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    d[i][j]=false;
                    if(i==j){
                        d[i][j]=true;
                    }
                }
                
            }
            //不能把它理解为无向图 ,因为 如果 a>b  a>c, a c不能判断 ,要把它理解为有向图 
            int a,b;
            for(int i=0;i<m;i++){
                scanf("%d %d",&a,&b);
                d[a][b]=true;
            }
            
    //        for(int i=1;i<=n;i++){
    //            for(int j=i+1;j<=n;j++){
    //                for(int k=i+1;k<j;k++){
    //                    d[i][j]=d[i][j]||(d[i][k]&&d[k][j]);
    //                }
    //            }
    //        }
            
            for(int k=1;k<=n;k++){
                for(int i=1;i<=n;i++){
                    if(d[i][k]==false){
                        continue;
                    }
                    for(int j=1;j<=n;j++){
                        d[i][j]=d[i][j]||(d[i][k]&&d[k][j]);
                    }
                }
            }
            
            cnt=0;
            for(int i=1;i<=n;i++){
                for(int j=i+1;j<=n;j++){
                    if(d[i][j]==false&&d[j][i]==false){
                        cnt++;
                    }
                }
            }
            cout<<cnt<<endl;
        }
    } 

     B-

    众所周知,TT 有一只魔法猫。

    今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!

    输入

    输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。

    下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。

    接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。

    下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。

    接下来 K 行是商业线路段的描述,格式同经济线。

    所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

    输出

    对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。

    本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行

    输入样例

    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

    思路分析:

       本题我使用的Dijskra算法来写的。Dijksra算法是一个求单源最短路的算法,使用于有向图和无向图,但是它只使用于边权为正的图,对于有负边权的图我们可以使用其他方法来解决,如Bellman-Ford算法(或其队列优化的SPFA算法)。对于Dijksra算法来说,一般来说它的复杂度是O(n^2),但是我们可以使用优先级队列来优化它,可以使它的总体复杂度是O(mlogn),n为顶点数,m为图的边数,当图不是那么稠密的时候这种优化还是有效的。

      Dijkstra适用于:解决单源、非负权重;最短路的松弛 :  dis[y] > dis[x] +w ,则松弛成功,更新 dis[y] 大小 ,将y加入小根堆中; 算法一定会结束,因为每个点只能够被小根堆弹出一次 。 每个点被弹出之后 ,dis[u] 即为最短路。

      对于本题,我们一开始不考虑使用商业票的情况,而是分别从起点和终点开始Dijskra算法,得到两个数组d1,d2。这时我们便可以得到不用商业票的耗费ans,若不连通则耗费无线大。然后我们在考虑使用商业票的情况,对于每一个商业票a,b,c(从a到b耗费c),此时的总耗费为d1【a】+c+d2【b】,然后于ans求小,同时记录下取小的商业票起点和终点,最后便能得到答案。

     以下是源代码:

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<cstring>
    #define maxn 1100
    #define INF 1000000
    
    using namespace std;
    
    
    struct Edge{
        int from,to,dist;
        Edge(int u, int v, int d):from(u),to(v),dist(d){}
    }; 
    
    struct HeapNode{
        int d,u;
        bool operator < (const HeapNode & rhs) const{return d > rhs.d;}
    };
    
    
    struct Dijkstra{
    
    
    int n,m;
    vector<Edge> edges;
    vector<int> G[maxn];
    
        bool done[maxn];
    //....
        void init(int nn){
            n=nn;
            for(int i=0;i<n;i++){
                G[i].clear();
            }
            edges.clear();
        }
        
        void addEdge(int from, int to, int dist){
            edges.push_back(Edge(from,to,dist));
            m=edges.size();
            G[from].push_back(m-1);
        }
        
        void dijkstra(int s,int d[],int p[]){
            priority_queue<HeapNode> Q;
            for(int i=1;i<=n;i++){ d[i]=INF; p[i]=-1;}
            d[s]=0;
            p[s]=-1;
            memset(done,false,sizeof(done));
            Q.push((HeapNode){0,s});
            while(!Q.empty()){
                HeapNode x=Q.top(); Q.pop();
                int u =x.u;
                if(done[u]) continue;
                done[u]=true;
                for(int i=0;i<G[u].size();i++){
                    Edge& e=edges[G[u][i]];
        //            cout<<"from="<<u<<"="<<e.from<<"to"<<e.to<<"and dist="<<e.dist<<endl;
                    if(d[e.to]> d[u]+ e.dist){
                        d[e.to]= d[u]+ e.dist;
        //                cout<<"change d["<<e.to<<"]="<<d[u]<<"+"<<e.dist<<endl;
                        p[e.to]= u;
                        Q.push((HeapNode){d[e.to],e.to});
                    }
                }
            }
    //        cout<<"dij=";
    //        for(int i=1;i<=n;i++){
    //            cout<<d[i]<<" ";
    //        }
    //        cout<<"pre4=";
    //        int temp=4;
    //        while(p[temp]!=-1){
    //            cout<<temp<<" ";
    //            temp=p[temp];
    //        }
    //        cout<<temp<<endl;
        }
    };
    
        int p1[maxn];
        int p2[maxn];
        int d1[maxn]={INF};
        int d2[maxn]={INF};
    vector<Edge> edges;
    
    int main(){
        int n,s,e;
        int M,K;
        Dijkstra D1;
        bool f=false;
        while(~scanf("%d%d%d",&n,&s,&e)){
            if(f==false){
                f=true;
            }else{
                printf("
    ");
            }
            scanf("%d",&M);
            int a,b,c;
            D1.init(n);
            for(int i=0;i<M;i++){
                scanf("%d%d%d",&a,&b,&c);
                D1.addEdge(a,b,c);
                D1.addEdge(b,a,c);
            }
            D1.dijkstra(s,d1,p1);
            D1.dijkstra(e,d2,p2);
            edges.clear();
            scanf("%d",&K);
            for(int i=0;i<K;i++){
                scanf("%d%d%d",&a,&b,&c);
                edges.push_back(Edge(a,b,c));
                edges.push_back(Edge(b,a,c));
            }
            int ans=d1[e];
            int buss=-1;
            int bussto=-1;
            for(int i=0;i<edges.size();i++){
        //        cout<<"d1"<<edges[i].from<<" "<<d1[edges[i].from]<<"d2"<<edges[i].to<<" "<<d2[edges[i].to]<<"DIST"<<edges[i].dist<<endl;
        //        ans=min(ans,D1.d[edges[i].from]+D2.d[edges[i].to]+edges[i].dist);
                if(ans>d1[edges[i].from]+d2[edges[i].to]+edges[i].dist){
                    ans=d1[edges[i].from]+d2[edges[i].to]+edges[i].dist;
                    buss=edges[i].from;
                    bussto=edges[i].to;
                }
            }
            
            if(buss==-1){
                int cnt=1;
                d1[0]=e;
                while(p1[e]!=-1){
                    d1[cnt]=p1[e];
                    e=p1[e];
                    cnt++;
                }
                printf("%d",d1[cnt-1]);
                for(int i=1;i<cnt;i++){
                    printf(" %d",d1[cnt-i-1]);
                }
                printf("
    Ticket Not Used
    ");
            }else{
                int cnt=1;
                d1[0]=buss;
                int temp=buss;
                while(p1[temp]!=-1){
                    d1[cnt]=p1[temp];
                    temp=p1[temp];
                    cnt++;
                }
                printf("%d",d1[cnt-1]);
                for(int i=1;i<cnt;i++){
                    printf(" %d",d1[cnt-i-1]);
                }
        //        cout<<buss;
                cnt=1;
                d2[0]=bussto;
                temp=bussto;
                while(p2[temp]!=-1){
                    d2[cnt]=p2[temp];
                    temp=p2[temp];
                    cnt++;
                }
                for(int i=0;i<cnt;i++){
                    printf(" %d",d2[i]);
                }
                printf("
    %d
    ",buss);
            }
            printf("%d
    ",ans);
        }
        
    }

    C-

    这一晚,TT 做了个美梦!
    
    在梦中,TT 的愿望成真了,他成为了喵星的统领!喵星上有 N 个商业城市,编号 1 ~ N,其中 1 号城市是 TT 所在的城市,即首都。
    
    喵星上共有 M 条有向道路供商业城市相互往来。但是随着喵星商业的日渐繁荣,有些道路变得非常拥挤。正在 TT 为之苦恼之时,他的魔法小猫咪提出了一个解决方案!TT 欣然接受并针对该方案颁布了一项新的政策。
    
    具体政策如下:对每一个商业城市标记一个正整数,表示其繁荣程度,当每一只喵沿道路从一个商业城市走到另一个商业城市时,TT 都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。
    
    TT 打算测试一下这项政策是否合理,因此他想知道从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请悄咪咪地打出 '?'。 

    Input

    第一行输入 T,表明共有 T 组数据。(1 <= T <= 50)
    
    对于每一组数据,第一行输入 N,表示点的个数。(1 <= N <= 200)
    
    第二行输入 N 个整数,表示 1 ~ N 点的权值 a[i]。(0 <= a[i] <= 20)
    
    第三行输入 M,表示有向道路的条数。(0 <= M <= 100000)
    
    接下来 M 行,每行有两个整数 A B,表示存在一条 A 到 B 的有向道路。
    
    接下来给出一个整数 Q,表示询问个数。(0 <= Q <= 100000)
    
    每一次询问给出一个 P,表示求 1 号点到 P 号点的最少税费。

    Output

    每个询问输出一行,如果不可达或税费小于 3 则输出 '?'。

    Sample Input

    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

    Sample Output

    Case 1:
    3
    4
    Case 2:
    ?
    ?

    思路分析:

      本题背景为可能有负边权的单源最短路问题,故dijkstra算法不再适用。我们使用Bellman−Ford算法或队列优化的SPFA算法。本题使用SPFA算法。

      首先完成图的输入,根据题意,先记录每个城市的权重WEI[] ,然后再完成图的边的加入,这里要注意的是加入边的时候边权是要根据题意计算得来的,易得边权可能为负边权。SPFA基本原理于Dijskra算法大同小异。先将起点到所有点的距离初始化为inf并将起点入队,当队列不为空时,每次取队首元素,将其出队并将vis值修改为0,进行松弛操作,其邻接点如果不在队列中将它加入队列。这里需要格外注意的是如果出现了负环路的问题。因为出现负环路的话直接就会导致最短路不存在(因为一直重复这个环路可能导致计算得出的最短路无穷小,故这种情况最短路不存在),那么我们如何判断是否出现了环路呢?我们可以使用一个计数数组cnt,当每次完成一次松弛操作的时候就加一,每次入队时判断这个点的cnt值是否超过了n-1,因为在一个不含负环的图中,每个点最多只能完成n-1次松弛操作,如果出现了上述情况,就说明了出现了负环,我们就不能再将它入队了。

     以下为源代码:

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<cstring>
    #include<cmath>
    #define maxn 300
    #define INF  1000000000
    
    using namespace std;
    
    
    struct Edge{
        int from,to,dist;
        Edge(int u, int v, int d):from(u),to(v),dist(d){}
    }; 
    
        bool j[maxn];
    
    struct SPFA{
    
    
    int n,m;
    vector<Edge> edges;
    vector<int> G[maxn];
    
        bool done[maxn];
    
        bool vis[maxn];
        int cnt[maxn];
        
        
        void init(int nn){
            n=nn;
            for(int i=1;i<=n;i++){
                G[i].clear();
            }
            edges.clear();
        }
        
        void addEdge(int from, int to, int dist){
            edges.push_back(Edge(from,to,dist));
            m=edges.size();
            G[from].push_back(m-1);
        }
        
        void spfa(int s,long long d[],int p[]){
                queue<int> Q;
                for(int i=1;i<=n;i++){
                    d[i]=INF;
                    p[i]=-1;
                    done[i]=false;
                    cnt[i]=0;
                    j[i]=true;
                }
                d[s]=0;
                done[s]=true;
                Q.push(s);
                
                while(!Q.empty()){
                    int u = Q.front(); Q.pop();
                    done[u]=false;
                    if(j[u]==false){
                        continue;
                    }
                    for(int i=0;i<G[u].size();i++){
                        Edge& e = edges[G[u][i]];
                        if(d[e.to]>d[u]+e.dist){
                            d[e.to] = d[u]+e.dist;
                            p[e.to]=u;
                            if(!done[e.to]&&j[e.to]){
                                
                                Q.push(e.to);
                                done[e.to]=true;
                                cnt[e.to]=cnt[u]+1;
                                if(cnt[e.to] > n){
                                //    d[e.to]=-INF;//发现有负圈 
                                    Bfs(e.to,j);
                                }
                            }
                        }
                    }
                }
            }
            
            void Bfs(int u,bool j[]){
                for(int i=1;i<=n;i++){
                    vis[i]=false;
                }
                j[u]=false;
                queue<int> Q;
                Q.push(u);
                vis[u]=true;
                while(!Q.empty()){
                    u=Q.front();Q.pop();
                    for(int i=0;i<G[u].size();i++){
                        int temp=edges[G[u][i]].to;
                        j[temp]=false;
                        if(vis[temp]==false){
                            vis[temp]=true;
                            Q.push(temp);
                                
                            }
                        }
                    }
            }
    };
    
        int p1[maxn];
        long long d1[maxn];
    
        int WEI[maxn];
        
    
    
    int main(){
        SPFA S;
        int T,N,M,Q;
        cin>>T;
        for(int ii=0;ii<T;ii++){
            cin>>N;
            S.init(N);
            for(int i=1;i<=N;i++){
                cin>>WEI[i];
            }
            cin>>M;
            int a,b,wei;
            for(int i=1;i<=M;i++){
                cin>>a>>b;
                wei=pow(WEI[b]-WEI[a],3);
                S.addEdge(a,b,wei);
        //        S.addEdge(b,a,-wei);
            }
            S.spfa(1,d1,p1);
            
    //        for(int i=1;i<=N;i++){
    //            cout<<"D[i] = "<<d1[i]<<" j"<<j[i]<<" ||";
    //        }
    //        cout<<endl;
        
            cin>>Q;
            cout<<"Case "<<ii+1<<":"<<endl;
            for(int i=1;i<=Q;i++){
                cin>>a;
                if(d1[a]<3||d1[a]==INF||j[a]==false){
                    cout<<"?"<<endl;
                }else{
                    cout<<d1[a]<<endl;
                }
            }
            
        }
        
        
    }

    end

  • 相关阅读:
    jenkins与gitlab集成,分支提交代码后自动构建任务(六)
    MySQL配置参数sync_binlog说明
    jenkins部署java项目(五)
    Mac OS X 中安装JDK 7
    centos 使用pip安装mysql-python
    CentOS6.4下安装TeamViewer8
    adb & adb shell 相关命令
    mac下限速
    mac 下 word 2011 使用笔记
    python twisted启动定时服务
  • 原文地址:https://www.cnblogs.com/Xu-SDU/p/12657355.html
Copyright © 2011-2022 走看看