zoukankan      html  css  js  c++  java
  • 洛谷P3008 [USACO11JAN]Roads and Planes G

     

     

    思路分析:

    解法一:

      由于这道题提到可能有负权边,那么dijsktra算法肯定首先就被我们排除了,floyd?想都不要想,最后我们只剩下了Spfa,但众所周知,USACO是卡Spfa的,但似乎没有其他的解法了,于是我们就可以像一道最短路板子题一样建图,从起点出发,跑一遍Spfa,交上去,果不其然,TLE掉了两个点。但也只有两个点,于是我们想到用SLF去优化它,所谓SLF,就是在Spfa入队时拿将要入队的数和队首元素比较,如果比队首元素小则插入队首,否则插在対尾,于是用到双端队列,每次入队前判断一下就好了。这样我们就可以在开O2的前提下水过这道题了,但不开O2还是会TLE掉一个点,其实这道题只是不想卡SLF,但SLF优化并没有基于复杂度,实际上可以构造数据来卡到2^n,甚至可能比普通spfa更慢(以上一句话来自洛谷大佬(id:1010_)),但既然能过,也是一种方法。

    上代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<queue>
     5 using namespace std;
     6 #define debug printf("------------
    ");
     7 const int N=1e6+10;
     8 int Head[N],tot,T,P,R,S,dis[N],vis[N];
     9 struct Node{
    10     int next,to,dis;
    11 }edge[N];
    12 void Add(int x,int y,int z){
    13     edge[++tot].to=y;
    14     edge[tot].next=Head[x];
    15     edge[tot].dis=z;
    16     Head[x]=tot;
    17 }
    18 void Spfa(int x){
    19     memset(dis,0x3f,sizeof(dis));
    20     memset(vis,0,sizeof(vis));
    21     deque<int>q;    //只有这里不同,其他都是板子 
    22     q.push_back(x);vis[x]=1;dis[x]=0;
    23     while(!q.empty()){
    24         int u=q.front();q.pop_front();
    25         for(int i=Head[u];i;i=edge[i].next){
    26             int v=edge[i].to;vis[u]=0;
    27             if(dis[v]>dis[u]+edge[i].dis){
    28                 dis[v]=dis[u]+edge[i].dis;
    29                 if(!vis[v]){
    30                     if(!q.empty()&&dis[v]>=dis[q.front()])
    31                         q.push_back(v);
    32                     else q.push_front(v);
    33                     vis[v]=1;
    34                 }
    35             }
    36         }
    37     }
    38 }
    39 int main(){
    40 //    freopen("a.txt","r",stdin);
    41 //    freopen("my.txt","w",stdout);
    42     scanf("%d%d%d%d",&T,&R,&P,&S);
    43     for(int i=1;i<=R;++i){
    44         int x,y,z;
    45         scanf("%d%d%d",&x,&y,&z);
    46         Add(x,y,z);Add(y,x,z);
    47     }
    48     for(int i=1;i<=P;++i){
    49         int x,y,z;
    50         scanf("%d%d%d",&x,&y,&z);
    51         Add(x,y,z);
    52     }
    53     Spfa(S);
    54     for(int i=1;i<=T;++i){
    55         if(dis[i]==0x3f3f3f3f)
    56             printf("NO PATH
    ");
    57         else printf("%d
    ",dis[i]);
    58     }
    59     return 0;
    60 }
    View Code

    解法二:

      虽然这道题明确说了会有负权,但那只是单向航道,双向的道路的权值仍然都是正值,所以在双向的道路内是符合dijsktra的使用前提的,于是我们想到缩点,我们可以把所有权值为正且相邻的点块缩在一起,这样就形成了一个DAG之后用拓扑排序在DAG上求解即可。需要注意的是,需要将不能互通的两点间距离初始化为一个很大的数,并在拓扑时要将所有入度为0的点算进去,防止出现不互达的情况。有了思路代码就不难写了,这样写能比解法一的时间快4倍左右。

    向量是真的难用调的我都要吐了

    上代码:

      1 #include<cstdio>
      2 #include<queue>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<vector>
      6 using namespace std;
      7 const int N=1e6+10;
      8 int T,R,P,S;
      9 int Head[N],tot,indeg[N];  //indeg表示节点入度个数 
     10 struct Node{
     11     int next,to,dis;
     12     bool c;   //c用来表示该边是双向路径还是单向航道 
     13 }edge[N];
     14 vector<int>ve[N];  //存储缩点后新节点编号所包含的节点编号 
     15 void Add(int x,int y,int z,int c){
     16     edge[++tot].to=y;
     17     edge[tot].next=Head[x];
     18     edge[tot].dis=z;
     19     edge[tot].c=c;
     20     Head[x]=tot;
     21 }
     22 int vis[N],belong[N],sum,dis[N];
     23 void dfs(int u,int num){  //缩点 
     24     vis[u]=1;
     25     ve[num].push_back(u);
     26     belong[u]=num;
     27     for(int i=Head[u];i;i=edge[i].next){
     28         int v=edge[i].to;
     29         if(!edge[i].c) continue;
     30         if(!vis[v]) dfs(v,num);
     31     }
     32 }
     33 struct Edge{
     34     int num,dis;
     35     Edge(int a,int b){
     36         num=a;dis=b;
     37     }
     38     bool operator < (const Edge& a)const{
     39         return a.dis<dis;
     40     } 
     41 };
     42 priority_queue<Edge>q;  //用于dijsktra计算 
     43 queue<int>Q;    //用于计算拓扑 
     44 void Topo_dijs(int x){   //拓扑和dijsktra结合 
     45     memset(dis,0x3f,sizeof(dis));
     46     memset(vis,0,sizeof(vis));
     47     for(int i=1;i<=sum;++i){
     48         if(!indeg[i]) Q.push(i);  //所有入度为0的点进行拓扑 
     49     }
     50     dis[x]=0;
     51     while(!Q.empty()){   //拓扑更新联通块 
     52         int k=Q.front();Q.pop();
     53         for(int i=0;i<ve[k].size();++i)
     54             if(dis[ve[k][i]]<0x3f3f3f3f) 
     55                 q.push(Edge(ve[k][i],dis[ve[k][i]])); //找起点 
     56         while(!q.empty()){   //对联通块内节点dijsktra
     57             Edge u=q.top();q.pop();
     58             //printf("%d
    ",u.dis);
     59             if(vis[u.num]) continue;
     60             vis[u.num]=1;
     61             for(int i=Head[u.num];i;i=edge[i].next){
     62                 int v=edge[i].to;
     63                 if(edge[i].c&&dis[v]>dis[u.num]+edge[i].dis){
     64                     dis[v]=dis[u.num]+edge[i].dis;
     65                     q.push(Edge(v,dis[v]));
     66                 }
     67                 else if(!edge[i].c){  //遍历到另一个联通块更新即可,无须入队 
     68                     dis[v]=min(dis[v],dis[u.num]+edge[i].dis);
     69                 }
     70             }
     71         }
     72         for(int j=0;j<ve[k].size();++j)  //更新下一个联通块 
     73         for(int i=Head[ve[k][j]];i;i=edge[i].next){
     74             int v=edge[i].to;
     75             if(edge[i].c) continue;
     76             if(--indeg[belong[v]]==0)
     77                 Q.push(belong[v]);
     78         }
     79     }
     80 }
     81 int main(){
     82     scanf("%d%d%d%d",&T,&R,&P,&S);
     83     for(int i=1;i<=R;++i){
     84         int x,y,z;
     85         scanf("%d%d%d",&x,&y,&z);
     86         Add(x,y,z,1);Add(y,x,z,1);
     87     }
     88     for(int i=1;i<=P;++i){
     89         int x,y,z;
     90         scanf("%d%d%d",&x,&y,&z);
     91         Add(x,y,z,0);
     92     }
     93     for(int i=1;i<=T;++i){  //割点 
     94         if(!vis[i]) dfs(i,++sum);
     95     }
     96     for(int j=1;j<=T;++j)
     97     for(int i=Head[j];i;i=edge[i].next){
     98         if(edge[i].c) continue;  //是缩点后的拓扑故双向路径不算 
     99         indeg[belong[edge[i].to]]++;
    100     }
    101     Topo_dijs(S);
    102     for(int i=1;i<=T;++i){  //统计答案 
    103         if(dis[i]==0x3f3f3f3f)
    104             printf("NO PATH
    ");
    105         else printf("%d
    ",dis[i]);
    106     }
    107     return 0;
    108 }
    View Code
  • 相关阅读:
    URL和URI之间的区别
    nom Uncaught Error: Cannot find module 'babel-runtime/regenerator'
    PHP中使用curl获取头信息headers的一些笔记
    Typora中自定义命令上传图片
    iOS链接big sur弹出“文件找不到”
    云开发cannot find module wx-server-sdk
    关于Laravel框架中Guard的底层实现
    PHP8中字符串与数字的比较更智能
    git refusing to merge unrelated histories
    k8s入门-资源文件实现
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/12808136.html
Copyright © 2011-2022 走看看