为防止广告,目前nocow只有登录用户能够创建新页面。如要创建页面请先登录/注册(新用户需要等待1个小时才能正常使用该功能)。

USACO/fence6

来自NOCOW
跳转到: 导航, 搜索
 这篇文章可以证实是由NOCOW用户原创,不存在任何版权争议。
 本文作者同意以GNU FDLCC-by-saGNU LGPL(如果适用)三种版权发布此文章(不包括翻译文章中属于原始所有者的部分版权)。
 如果你修改了这篇文章并且不同意用GNU FDL以外的版权发布,可以换一个版权模板或者移除此模板。
这是USACO Chapter 4 .1中的OI题目Fence Loops介绍及题解,参见 翻译C语言代码C++语言代码Pascal语言代码


目录

[编辑] 做法一

求无向图中的最小环。

算法很简单,就是做m遍dijkstra——每次找到一条边,拿掉,求这条边两个顶点之间的最短路,那么如果这两点间存在最短路,则这条路径与原来的边就构成了一个环。这样所有环中最小的一个就是答案。

问题是题目给出的是边连通的信息而非点连通。也就是说我们得到的信息无法按照常规的方法(邻接矩阵,邻接表)来构图。这里就需要一个转化。由于我想不到什么好的算法,所以就用了一个复杂度为O(n^2)的转化。首先将每条边的两个顶点都看作单独的点(也就是说假设所有边都不连通,为了方便,可以分别设第i条边的两个顶点编号为i*2-1和i*2),然后对于两条连通的边,将连通这两条边的点并在一起。具体做法就是将其中一个点的连通情况全部赋给另一个点,并修改图中其他与该点连通的点信息使得合并成立。这里我借助了并查集,使得每次查找的时间都近似为常数,所以总的时间复杂度就是O(n^2)。其中n是合并后总的点的个数

经过上述转化以后,再用m遍dijkstra,总的算法时间复杂度就是O(mn^2)。

[编辑] 做法二

这道题给的数据是边之间的关系,首先想到的是构图,然后套经典的最短路做.但是这道题的数据输入会使按点构图很麻烦,其实这道题搜索即可,而且加剪枝后很快.由v条边开始搜索在它b方向的边,重复这个过程,直到回到v而且是由a方向回到的,为了判断环可以加个判重,一个剪枝:对于路径长度大于已知最小环的可剪.

[编辑] 做法三

一种比较诡异的方法。就是把边变成“点”。在把“边”的长度设定为边两端的“点”(既原来的边)的长度的和。

用由floyd算法改进的求最小环的算法求最小环。注意的是枚举“点”时,注意“点”连接的两“点”要是分别连接到该“点”代表的边的两个端点。

这个复杂度为O(n^3)。超级快…...

证明略……有兴趣的自己想一下

参考程序:Pascal程序代码


这个做法和我的相似。我补充一下。把篱笆看成是图的顶点,在建图的时候将每一条边的权值赋为它与相邻篱笆的长度和(也就是说每个顶点引出n1s+n2s条边)。

用floyed求最小环的方法求出长度,除以2就是结果。

yuhc


(这个方法是错误的,还是无法避免篱笆交于一点的情况)

话说这是个很纠结的办法……我也是一开始就想的这种方法……但是发现了它很难处理的一个情况—————就是几个篱笆交于一点。

解决办法就是:在求最小环的时候,拿邻接表(边表也可)存储下两端的篱笆,然后分别枚举两端的篱笆,就可以解决这个问题了!

另外本人力挺这种做法!

————SKYWALKER

与此方法类似,构图时I建立2*I和2*I-1两个点,然后将相连的点幷查集,最后套用经典的最小环求法


最初那种直接把点看成边的方法不正确! 如样例中如果把相连的边变成点后都连上一条边,那么就会把一前不是环的路径变成环,造成错误答案: 如: 样例2,3,8三边不成环,但是当作点连接后就是环了,结果为10,错误!

是啊,卡了好久T_T!!!

其实是可以的 出现LS的情况其实是没有体现题目中数据的方向性 我们把与他左端相连的开个邻接表 右端相连的开个邻接表

这么着就可以避免了

什么边点互化,我弄了好久都没过。

[编辑] 做法四

仍然是求最小环,先将边邻接矩阵转为点邻接矩阵,再dfs求出所有环。

可以证明时间复杂度为O(n^3),所以能快速AC

还是DFS好 代码也简单

[编辑] 做法五

啥剪枝都不用,直接按边暴力DFS- -唯一需要小心的是找下一条边的时候要注意在另一侧.

[编辑] 做法六

二分答案,也是秒杀ac

哪位能解释一下二分的具体做法?我想不到诶…

回LS,就是迭代加深

[编辑] 做法七

做法三做些改变之后的方法,更加易懂

给交点编号,就使其可以像正常图一样求最小环

用的floyd求最小环


[编辑] 做法八

不用把栅栏当作整体来看,题目没有那么复杂。如果你把栅栏的两端当作两个点,而栅栏当作连接它们的边或路径的话,你会发现,这其实就是一个最短路的问题。但并不是裸的,操作的时候要注意下面几点:

1、题目里要求的是最短的环,转化之后就是求彼此距离最短的在同一栅栏两端的点

2、两段栅栏相接处在题目中是同一个点,但在程序中你可以把它当作分开的点,之间的距离为0,方便做最段路

3、直接用FLOYD做是不行的。对此我已经有了失败的经验。正确的做法是一对一对顶点求,然后不断更新最小值,每次求之前应该把栅栏上的值赋为无穷大,然后把求出来的最短路值加上栅栏上的值、就是这对顶点间的最小环

PS:这样做的复杂度很可观、当中的最短路用SPFA做的话可以在0.00秒AC全部数据

OVER ——H`


[编辑] 做法九

Executing...

  Test 1: TEST OK [0.003 secs, 3836 KB]
  Test 2: TEST OK [0.005 secs, 3836 KB]
  Test 3: TEST OK [0.008 secs, 3836 KB]
  Test 4: TEST OK [0.008 secs, 3836 KB]
  Test 5: TEST OK [0.008 secs, 3836 KB]
  Test 6: TEST OK [0.008 secs, 3836 KB]
  Test 7: TEST OK [0.005 secs, 3836 KB]
  Test 8: TEST OK [0.005 secs, 3836 KB]
  Test 9: TEST OK [0.003 secs, 3836 KB]

All tests OK.

我自己想到非常诡异的算法!时间复杂度O(n^2)!!!!

1. 以篱笆为边构图

2. prim最小生成树生成一棵树

记下sum[i][j]表示生成树中i点到j点的距离 fa[i]表示i节点在生成树中的父亲编号 则 设i为当前遍历到的节点则

{

  sum[i][k]=sum[k][i]=sum[fa[i]][k]+val[i][fa[i]];

}

3. 枚举每一条非生成树中的边 此时它连接的两个在生成树中的顶点之间的路径和它将形成一个环(类似于次小生成树) 然后找最小环就可以了!!!! 快得飞起类!!!好简单好好写的类!!!代码超级短类!!!不用跑最短路了类!!!觉得好的点个赞类!!!好算法求不删类!!! --- by LYH

[编辑] Problem?

Is there anyone who's ever said the fence is straight? If not, simple minial-circle might return edges that enclosed more than one pasture. like this:

  -  fence1
 / \ fence2,3
 --- fence4
/    \ fence4
------fence5
\____/fence 6,7,8

[编辑] 参考代码

C

C++

Pascal

Java

个人工具