• 题目背景:

    幻想乡的亡灵公主,西行寺幽幽子,在幻想乡很受欢迎,经常有妖怪来拜访她,但是幽
    幽子并不喜欢被打扰,她希望从白玉楼出发,散步之后再回到白玉楼,同时路上遇到的妖怪越少越好(有趣的是道路两边的妖怪数量并不相同,分别从两个方向经过同一条道路遇到的妖怪数量是不同的)。当然,作为冥界的公主,她是不会重复经过同一条道路的。

    问题描述:

    给定一个有 n 个点 m 条无向边的图,每条无向边最多只能经过一次。
    对于边(ui, vi), 从 ui 到 vi 的代价为 ai,从 vi 到 ui 的代价为 bi,其中 ai 和 bi 不一定相等。 求一个包含 1 号点的有向环,使得环上代价之和最小。(保证图中没有重边和自环。)

    输入格式:

    第一行两个个正整数 n,m,点数和边数。
    接下来 m 行,每行四个正整数,ui,vi,ai,bi。
    从 ui 到 vi 的代价为 ai,从 vi 到 ui 的代价为 bi 。

    输出格式:

    输出一行,一个整数,如果有解输出最小代价,否则输出”-1”(不含引号)。

    样例数据 1:

    zaw.in
    3 3
    1 2 4 3
    2 3 4 2
    1 3 1 1
    zaw.out
    6

    样例数据 2:

    zaw.in
    3 2
    1 2 12 5
    3 2 10 5
    zaw.out
    -1

    数据范围:
    对于前 15% 的数据,3 <= n <= 20, 3 <= m <= 20
    对于前 30% 的数据,3 <= n <= 150, 3 <= m <= 2500
    对于前 70% 的数据,3 <= n <= 5000, 3 <= m <= 10^4
    对于 100% 的数据, 3 <= n <= 3*10^4, 3 <= m <= 10^5
    1<=ui, vi <= n,1 <= ai, bi <= 10^4。
    保证图中没有重边,即不存在 i <> j,使得 ui = uj,vi = vj。
    保证图中没有自环,即 ui <> vi。

    【题解】

    算法一:
    搜索
    期望得分: 15 分

    算法二:
    启发式搜索
    期望得分: 15 ~ 100 分

    算法三:
    枚举从 1 号点出发到达的第一个点 x 和回到 1 号点之前经过的的最后一个点 y。
    那么有向环 1 -> x -> y -> 1 的代价为 w(1, x) + dist(x, y) + w(y, 1) 。
    其中 dist(x, y)为不经过 1 号点,从 x 到 y 的代价。显然可以用最短路解决。
    时间复杂度: O(n * n * SPFA(n,m)) / O(n^3)
    期望得分: 30 分

    算法四:
    我们发现算法三的瓶颈在于求 dist(x, y),其实我们只要求 n 次单源最短路就可以了。
    时间复杂度: O(n * SPFA(n,m)) / O(n * n * log n)
    期望得分: 70 分

    算法五:
    我们发现算法四的瓶颈在于枚举 x 和 y。
    事实上,我们可以把除 1 号点以外的点分成两个集合 ,X 和 Y。
    然后通过一次最短路求出有向环 1 -> x ∈ X -> y ∈ Y -> 1 的最小代价。
    那么按二进制位分组,考虑二进制下第 i 位。
    1.第 i 位为 0 的点划分到 X 集合,第 i 位为 1 的点划分到 Y 集合,然后跑最短路更新答案。
    2.第 i 位为 1 的点划分到 X 集合,第 i 位为 0 的点划分到 Y 集合,然后跑最短路更新答案。
    我们知道只要任意点对都至少被分别划分到两个集合中一次,就可以得到答案。
    而两个数至少有一个二进制位不相同,所以任意点对都至少被划分一次。
    时间复杂度: O(log n * SPFA(n, m)) / O(n * log n * log n)
    期望得分:100 分

    不过经试验,出题人题解的满分算法会TLE到2.33s,但出题人的算法4反而在删掉没用东西后能AC。。。

    找到节点1所有的相邻节点,dis[t]初值设为a[x].w,然后跑一遍t->1的SPFA,加上最优性剪枝即可。

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define fp(i,a,b) for(int i=a;i<=b;i++)
    #define fq(i,a,b) for(int i=a;i>=b;i--)
    #define il inline
    #define ll long long 
    using namespace std;
    const int maxN=30005;
    const int maxM=100001*4;
    const int inf=214748364;
    struct Edge
    {
        int u,v,w,next;
    }e[maxM];
    int n,m,ans=inf,cnt,head,dis[maxN],q[maxM*10],tail,h[maxN];
    bool inq[maxN];
    il int gi()
    {
       int x=0;
       short int t=1;
       char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    void add(int u,int v,int w)
    {
        e[++cnt]=(Edge){u,v,w,h[u]};h[u]=cnt;
    }
    il void spfa(int st,int initw)//模板
    {
        memset(dis,127,sizeof(dis));
        memset(inq,0,sizeof(inq));
        dis[st]=initw;
        q[tail++]=st;
        inq[st]=1;
        while(head<tail)
        {
    
            int u=q[head++];
            inq[u]=0;
            if(dis[u]>=ans)
            {
                inq[u]=0;
                continue;
            }
            if(u==1)
            {
                ans=dis[u];
                inq[u]=0;
                continue;
            }//最优性剪枝
           for(int i=h[u];i!=-1;i=e[i].next)
            {
                 int v=e[i].v;
                if(u==st&&v==1) continue;
                if(dis[u]+e[i].w<dis[v])
                {
                    dis[v]=dis[u]+e[i].w;
                    if(!inq[v])
                    {
    
                        q[tail++]=v;
                        inq[v]=1;
                    }
                }
            }
        }
    }
    int main()
    {
        freopen("zaw.in","r",stdin);
        freopen("zaw.out","w",stdout);
        memset(h,-1,sizeof(h));
        n=gi();m=gi();
        fp(i,1,m)
        {
            int u=gi(),v=gi(),a=gi(),b=gi();
            add(u,v,a);
            add(v,u,b);
        }
        ans=inf;
        for(int i=h[1];i!=-1;i=e[i].next)
            spfa(e[i].v,e[i].w);
        if(ans==inf) printf("-1
    ");
        else printf("%d
    ",ans);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }

    最后,把大佬的程序(也是跑的最快的)放在这里,看到时候是否能看懂。

    // MADE BY QT666
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=500050;
    const int Inf=19260817;
    int cnt=1,n,m,head[N],to[N],nxt[N],c[N],in[N],vis[N],ans=Inf,vis2[N];
    int W[N];
    struct Data{
      int fir,sec,ffrom,sfrom;
    }dis[N];
    struct data{
      int x,fa,from;
    }q[N*2];
    void lnk(int x,int y,int v,int z){
      to[++cnt]=y,c[cnt]=v,nxt[cnt]=head[x],head[x]=cnt;
      to[++cnt]=x,c[cnt]=z,nxt[cnt]=head[y],head[y]=cnt;
    }
    void spfa(){
      int t=0,sum=1;q[0].x=1;dis[1].fir=dis[1].sec=0;
      while(t<sum){
        data now=q[t++];
        for(int i=head[now.x];i;i=nxt[i]){
          if(i!=(now.fa^1)||now.fa==0){
        int y=to[i],flag=0;
        if(now.x!=1){
          if(dis[now.x].fir+c[i]<dis[y].fir){
            if(dis[y].fir<dis[now.x].sec+c[i]){
              if(dis[y].ffrom!=dis[now.x].ffrom){
            dis[y].sec=dis[y].fir;dis[y].sfrom=dis[y].ffrom;
            dis[y].fir=dis[now.x].fir+c[i];dis[y].ffrom=dis[now.x].ffrom;
            flag=1;
              }
              else{
            if(dis[y].sec<dis[now.x].sec){
              dis[y].fir=dis[now.x].fir+c[i];
              dis[y].ffrom=dis[now.x].ffrom;flag=1;
            }
            else{
              if(dis[y].ffrom!=dis[now.x].sfrom){
                dis[y].fir=dis[now.x].fir+c[i];
                dis[y].ffrom=dis[now.x].ffrom;
                dis[y].sec=dis[now.x].sec+c[i];
                dis[y].sfrom=dis[now.x].sfrom;flag=1;
              }
            }
              }
            }
            else{
              dis[y].fir=dis[now.x].fir+c[i];
              dis[y].ffrom=dis[now.x].ffrom;
              dis[y].sec=dis[now.x].sec+c[i];
              dis[y].sfrom=dis[now.x].sfrom;
            }
          }
          else {
            if(dis[y].sec<dis[now.x].sec+c[i]&&dis[y].sec>dis[now.x].fir+c[i]){
              if(dis[y].ffrom!=dis[now.x].ffrom){
            dis[y].sec=dis[now.x].fir+c[i];
            dis[y].sfrom=dis[now.x].ffrom;flag=1;
              }
            }
            else if(dis[y].sec>dis[now.x].sec+c[i]){
              if(dis[y].ffrom!=dis[now.x].ffrom){
            dis[y].sec=dis[now.x].fir+c[i];
            dis[y].sfrom=dis[now.x].ffrom;flag=1;
              }
              else if(dis[y].ffrom!=dis[now.x].sfrom){
            dis[y].sec=dis[now.x].sec+c[i];
            dis[y].sfrom=dis[now.x].sfrom;flag=1;
              }
            }
          }
        }
        if(flag||now.x==1) q[sum++]=(data){y,i};
          }
        }
      }
    }
    int main(){
      freopen("zaw.in","r",stdin);
      freopen("zaw.out","w",stdout);
      scanf("%d%d",&n,&m);
      for(int i=1;i<=m;i++){
        int x,y,v,z;scanf("%d%d%d%d",&x,&y,&v,&z);
        lnk(x,y,v,z);
      }
      for(int i=2;i<=n;i++) dis[i].fir=dis[i].sec=Inf;
      for(int i=head[1];i;i=nxt[i]){
        if(dis[to[i]].fir==Inf){
          dis[to[i]].fir=c[i];
          dis[to[i]].ffrom=i;
        }
        else{
          if(dis[to[i]].fir>c[i]){
        dis[to[i]].sec=dis[to[i]].fir;
        dis[to[i]].sfrom=dis[to[i]].ffrom;
        dis[to[i]].fir=c[i];
        dis[to[i]].ffrom=i;
          }
          else{
        if(dis[i].sec>c[i]){
          dis[to[i]].sec=c[i];
          dis[to[i]].sfrom=i;
        }
          }
        }
      }
      spfa();
      for(int i=head[1];i;i=nxt[i]){
        int y=to[i];
        if(dis[y].ffrom!=i) ans=min(ans,dis[y].fir+c[i^1]);
        else ans=min(ans,dis[y].sec+c[i^1]);
      }
      if(ans==Inf) puts("-1");
      else printf("%d",ans);
    }
    
  • 相关阅读:
    Zookeeper的ZAB协议
    Netty从入门到放弃,从放弃在到入门
    Java多线程-锁的原理
    ContextLoaderListener的说明
    Jdk和Cglib 的区别
    zookeeper核心概念
    https
    [CS Academy] Infinity Array
    [JZOJ 5669] Permutaition
    [CF 613 Div.1E] Puzzle Lover
  • 原文地址:https://www.cnblogs.com/yanshannan/p/7413145.html
Copyright © 2020-2023  润新知