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 }
原文地址:https://www.cnblogs.com/lsgjcya/p/8414183.html