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快出许多

  • 相关阅读:
    oracle中Blob和Clob类型的区别
    为什么要分库分表
    Enable file editing in Visual Studio's debug mode
    SQL Server Dead Lock Log
    Debug .NET Framework Source
    SQL Server text field里面有换行符的时候copy到excel数据会散乱
    诊断和修复Web测试记录器(Web Test Recorder)问题
    Can't load Microsoft.ReportViewer.ProcessingObjectModel.dll
    'telnet' is not recognized as an internal or external command
    Linq to XML
  • 原文地址:https://www.cnblogs.com/tclan126/p/8492746.html
Copyright © 2011-2022 走看看