zoukankan      html  css  js  c++  java
  • 2.4模拟赛

    今天考试像中了毒一样,第二题能A忘记换行,第一题编译错误(虽然成功了也没分...),又是小菜鸡成功爆0的一天。

    总结一下今天的第三题  HNOI2009最小圈   二分+dfs版spfa判断负环

    这道题自己做的时候压根没有想到用二分来实现。看到环直接想到了tarjan,还是太菜了。然鹅tarjan只能完成缩点,并且一缩就缩走一个大环,对于这道题而言,要考虑小环以及要算环权,并不清楚能不能实现(作为蒟蒻当然是不会的)。

    根据神佬的指点,可以这样来考虑。

    当我们拿到这个题目,图上的问题,这时候我们可以想到最短路算法,但是最短路算法是无后效性的,即后面的操作不会影响到前面的最优性。但是这道题是有后效性的,选了当前最优方案后整体不一定为最优方案。我们想到拓扑将不在环上的点去掉,for循环扫环,算答案后去掉,但是我们又想到有环可能会重用一条边,这样缩去一个环后也可能会去掉另一个环的某条边。想到用dp的话不好定义状态,n为3000,开二维空间勉强够,三维GG,时间复杂度也不好估计……这时候我们发现我们很难从所有解中直接找出最优解,我们靠神秘力量(多做题可以积攒神秘力量)思考到我们可以考虑将其转化为判断可行性问题,由从众多解中直接求得最优转化为求所有解中某些可行解。考虑用随机化或者二分来做。这时候我们可以思考二分答案的方法,二分答案的重点在于如何判断该答案的可行性。结合题目中的环的最小平均值,我们考虑到一个环的每条边权值-该环平均权值后成为一个“0环”。dfs可以用来判断环,因为深搜是一路搜下去,若我们搜到了他的某一个祖先(若该图为无向图,则父亲不包括在内),则有环。用spfa的思想,当这个点可以更新其祖先的dis数组时,环为负环。这时候我们的思路大致就清晰了。代码比较简单实现,需要注意题目中的一个点。“有一个点可以到达其他所有点”,这句话说明我们不能随意以一个点作为dfs的起点,因为他不一定能访问到负环,这时候我们 dfs n次,当有一次成功后可break掉。

    代码自行理解,应该不是太难。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int n,m,cnt,flag;
     6 int head[10010];
     7 bool vis[10010];
     8 double dis[10010];
     9 struct node{
    10 int to,next;double v;
    11 }edge[10010];
    12 int read()
    13 {
    14     int x=0,w=1;char ch=getchar();
    15     while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
    16     while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    17     return x*w;
    18 }
    19 void add(int x,int y,double v)
    20 {
    21     cnt++;
    22     edge[cnt].to=y;
    23     edge[cnt].next=head[x];
    24     edge[cnt].v=v;
    25     head[x]=cnt;
    26 }
    27 void spfa(int k,double jian)
    28 {
    29     vis[k]=1;
    30     int v;
    31     for(int i=head[k];i;i=edge[i].next)
    32     {
    33         v=edge[i].to;
    34         if(dis[v]>dis[k]+edge[i].v-jian)
    35         {
    36             if(vis[v])
    37             {
    38                 flag=1;
    39                 return;
    40             }
    41             dis[v]=dis[k]+edge[i].v-jian;
    42             spfa(v,jian);
    43             if(flag) return;
    44         }
    45     }
    46     vis[k]=0;
    47 }
    48 bool check(double mid)
    49 {
    50     flag=0;
    51     memset(dis,0x3f,sizeof(dis)); //这里注意进入dfs时应当所有点的dis值一样,0,0x3f……都可以。这样才能保证搜到了的是负环。(仔细想想这里,若如spfa那样第一个点为0,则有可能找到的环不是负环。本人表述能力有限,尽量理解)
    52     memset(vis,0,sizeof(vis));
    53     for(int i=1;i<=n;i++)
    54     {
    55         spfa(i,mid);
    56         if(flag) return 1;
    57     }
    58     return 0;
    59 }
    60 int main()
    61 {
    62     int x,y;
    63     double z;
    64     n=read();m=read();
    65     for(int i=1;i<=m;i++)
    66     {
    67         x=read();y=read();
    68         scanf("%lf",&z);
    69         add(x,y,z);
    70     }
    71     double l=-10000000,r=10000000,mid;
    72     while(r-l>1e-10)
    73     {
    74         mid=(l+r)/2;
    75         if(check(mid))
    76         {
    77             r=mid;
    78         }
    79         else
    80         {
    81             l=mid;
    82         }
    83     }
    84     printf("%.8lf",mid);
    85     return 0;
    86 }
  • 相关阅读:
    2. 两数相加
    1. 两数之和
    x-pack elasticsearch
    简单的文档
    PHP imagepng函数 问题
    Nginx 配置
    nginx内置变量
    TCP通信
    mysql 的一些操作
    ubuntu 软件包降级
  • 原文地址:https://www.cnblogs.com/lsgjcya/p/8414183.html
Copyright © 2011-2022 走看看