zoukankan      html  css  js  c++  java
  • 1003. Emergency (25)

    还有16天PAT考试最终要的是做透每一题

    (1)

    思路:两次dfs

    第一次找到最短路径的长度

    第二次在最短路径的前提下找到最大的救护资源数

    #include <cstdio>
    #include <climits>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int M=550;
    int n,m,c1,c2;
    int res[M];
    int v[M][M];
    char vis[M];
    int numpath=0;
    int maxres=0;
    int minpath=INT_MAX;
    
    void dfs1(int c,int path) {
      if(c == c2) {
        minpath=min(path,minpath);
        return;
      }
      for(int i=0;i<n;i++) {
        if(i!=c && vis[i] == 0 && v[i][c] != INT_MAX) { //访问没有访问过的相联的其他节点
          vis[i]=1;
          dfs1(i,path+v[i][c]);
          vis[i]=0;
        }
      }
    }
    
    void dfs2(int c,int re,int path) {
      if(c == c2) {
        if(minpath == path) {
          maxres=max(maxres,re); 
          numpath++;
        }
        return;
      }
      for(int i=0;i<n;i++) {
        if(i!=c && vis[i] == 0 && v[i][c] != INT_MAX) { //访问没有访问过的相联的其他节点
          vis[i]=1;
          dfs2(i,re+res[i],path+v[i][c]);
          vis[i]=0;
        }
      }
    }
    
    int main() {
      scanf("%d %d %d %d",&n,&m,&c1,&c2);
    
      memset(res,0,sizeof(res));
      memset(vis,0,sizeof(vis));
      for(int i=0;i<M;i++){
        for(int j=0;j<M;j++){
          if(i == j) v[i][j]=0;
          else v[i][j]=INT_MAX;
        }
      }
      for(int i=0;i<n;i++) {
        scanf("%d",&res[i]);
      }
      //初始化图
      for(int i=0;i<m;i++) {
        int c3,c4,w;
        scanf("%d %d %d",&c3,&c4,&w);
        v[c3][c4]=w;
        v[c4][c3]=w;
      }
    
      vis[c1]=1;
      dfs1(c1,0);
      dfs2(c1,res[c1],0);
      printf("%d %d",numpath,maxres);
      return 0;
    }

    自己用的g++ + emacs的环境在写,这次dfs有个比较 本来是 == 不小心 写成了 =  ,偷懒没有加-Wall选项结果人肉debug了半天,以后得注意这一点才行

    不过到时候考试用vs应该就不会出现这种情况吧

    (2)

    思路dijkstra算法

    1.将dst各位初始位无穷dst[c1]为0

    2.分为两个集合一个是已经确定最小距离的的点的集合P一个是还未确定最小距离的点Q

    3.每次从Q中取最小的出来,它一定就是已经确定最小距离的点(原理如下(*))将它放入P

    4.从该点取其相邻进行松弛

    5.返回3.

    (*)

                             

              s----------3---------v

                 2                 /  -5

                            u

    比如到u的最小距离,我们按照上面的做法会取2这条边因为它是s的边中最小的

    如果在无负权的的情况下从其他位置转到u的一定会比这个长,但是这里有负权,结论就不成立了,这也是dijkstra不适合带负权的图的原因

    #include <cstdio>
    #include <cstring>
    #include <climits>
    
    using namespace std;
    
    const int M=510;
    int dst[M];
    int book[M];
    int res[M];
    int num[M];
    int weight[M];
    const int inf=99999;
    int ve[M][M];
    int n,m,c1,c2;
    int cnt=1;
    
    void dij() {
      while(cnt < n) { 
        int mindst=inf;
        int v;
        for(int i=0;i<n;i++) {
          if(book[i] == -1) {
        if(dst[i] < mindst) {
          mindst=dst[i];
          v=i;
        }
          }
        }
        book[v]=0;
        cnt++;
        for(int i=0;i<n;i++) {
          if(ve[v][i] != inf && book[i] == -1) {
        if(dst[i] > dst[v]+ve[v][i]) {
          dst[i]=dst[v]+ve[v][i];
          num[i]=num[v];
          weight[i]=weight[v]+res[i];
        } else if(dst[i] == dst[v]+ve[v][i]){
          num[i]+=num[v];
          if(weight[v]+res[i] > weight[i])
            weight[i]=weight[v]+res[i];
        }
          }
        }
      }
    }
    
    int main() {
      scanf("%d %d %d %d",&n,&m,&c1,&c2);
      memset(res,0,sizeof(res));
      memset(book,-1,sizeof(book));
      memset(num,0,sizeof(num));
      memset(weight,0,sizeof(weight));
      //初始化图
      for(int i=0;i<M;i++){
        for(int j=0;j<M;j++){
          if(i == j) ve[i][j]=0;
          else ve[i][j]=inf;
        }
      }
      for(int i=0;i<n;i++) {
        scanf("%d",&res[i]);
        dst[i]=inf;
      }
      for(int i=0;i<m;i++) {
        int c3,c4,w;
        scanf("%d %d %d",&c3,&c4,&w);
        ve[c3][c4]=ve[c4][c3]=w;
        //      if(c3 == c1) dst[c4]=w;
        //      if(c4 == c1) dst[c3]=w; 不能加这两句,因为这会少两次松弛
        //使得num不能正确的初始化
      }
    
      dst[c1]=0;
      weight[c1]=res[c1];
      //  book[c1]=0; 不能加这一句不然刚开始就不是从c1开始的了
      num[c1]=1;
      dij();
    
      printf("%d",num[c2]);
      printf(" %d
    ",weight[c2]);
      return 0;
    }

    针对这一题关注一下 这里的num数组和weight数组

    num[i]为节点i到源点的最短的路径的数量,weight[i]为节点i到源点的最短路径的最大资源数

    dijkstra是用来计算单源最短路径的,这里我们还要计算最小路径的数量已经最小路径下最大的点权值

    num和weight数组就是用来实现这一点的

    考虑以下情况

    (1) 某个点i的距离需要松弛  if(dst[i] > dst[v]+ve[v][i]) 

    num[i]=num[v]

    (2)某个点i的距离不需要松弛 dst[i] == dst[v]+ve[v][i]

    num[i]+=num[v];

    为什么这样可以得到

    首先,若松弛后不是最小距离,num[i]以后一定会被替换掉,最后一定得到的是最小距离情况下的num

    其次(2)的情况下,会加上新的路径数

    同理weight数组在(1)的情况下最后一定会被最小距离情况的weight替换

    而(2)的情况下如果weight值得到了更大的那么就替换掉

    可以看到最后一个节点比两次dfs快出许多

  • 相关阅读:
    python基础之列表深浅复制的问题
    跟着阿里学JavaDay07——Java基础语法(五)
    Java培训Day03——制作疫情地图(三)
    Java培训Day02——制作疫情地图(二)
    Java培训Day01——制作疫情地图(一)
    跟着阿里学JavaDay06——Java基础语法(四)
    跟着阿里学JavaDay05——Java基础语法(三)
    跟着阿里学JavaDay04——Java基础语法(二)
    跟着阿里学JavaDay03——Java基础语法(一)
    跟着阿里学JavaDay02——Java编程起步
  • 原文地址:https://www.cnblogs.com/tclan126/p/8492746.html
Copyright © 2011-2022 走看看