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("\n");
        }
        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("\nTicket Not Used\n");
        }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("\n%d\n",buss);
        }
        printf("%d\n",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

posted on 2020-04-17 15:31  FFchopin  阅读(110)  评论(0编辑  收藏  举报

导航