【数据结构】量子危机

问题

宇宙时间公元 5.55 亿年,由于某种原因两大联盟展开了激战(maxingc 联盟采用了微子技术):
邪恶的 maxingc 联盟采集好了微子能,就要运输。Maxingc 联盟的领袖 xc 此时才发现,自己的军事基地中
由微子发射器组成的微子能量网存在很大的问题,于是他决定修改。
之前,TT 为了整齐,把军事基地建成了矩形,而且如果两个微子发射器的连线平行于军事基地的一边,这
两个微子发射器之间就一定有微子能量传输线相连。
(*注:比如有 3 个微子发射器A(1,1)、B(1,3)、C(2,2),那么 A 和 B 之间有微子能量传输线相连,
A 和 B 不能传输到 C。*)
但是在微子能运输过程中发现,常常不能从一个微子发射器运抵另一个微子发射器。
为了可以从任何一个微子传输器能运抵其它任意一个微子传输器,而且能和原来的微子能量网同样整齐,
TT 决定遵循原来的规则,调动他的百万农奴新修建一些微子能量传输线和微子发射器。由于微子发射器的
造价比微子传输线高得多,所以 TT 决定忽略微子能量传输线的成本。
但是 TT 又不想花费不必要的钱,所以找到你为他计算最少需要建多少个微子发射器。
输入格式  Input Format
第一行三个正整数 n、m、p。(2<=n,m<=100000,表示军事基地的两边长;2<=p<=200000,表示微子

发射器的个数。)
接下来 p行,每行两个正整数数 Xi、 Yi(1<=Xi<=n   1<=Yi<=m),代表每个微子发射器在军事基地的位置。
(可能会由于疏漏,有些微子发射器重复)
输出格式  Output Format
样例:
 输入:5 6 6
1 1
2 2
2 4
3 3
5 1
5 5
输出:
2
只有一行,为最少修建微子发射器的数量。
样例的一种方案:
#.....
|#-#..
|.#+..
|.|...
#-+-#.
#表示原有微子发射器,-、|表示微子能量传输线,+表示兴建的微子发射器。所以至少兴建 2 个微子发射
器。

分析

我们先这样考虑,如果对所有已知的节点进行染色的话,能染成同一种颜色的节点一定在同一个强连通分量中(内部连同),那么需要兴建的节点数就等于强连通分量数减一

我们来证明这个结论:对于任意一个节点它可以照顾到一行一列上的所有节点。也就是说,一个节点最多可以连接到4个强连通分量。如果一个兴建的节点只连接一个强连通分量,那么这个节点是毫无用处的。如果一个节点连接3个强连同分量,那么这3个强连通分量必有两个已经在一个强连通分量中。连接4个同理。只有当一个节点连接两个强连通分量时,才能让这两个原先不连通的分量相互连同。

这就类似于最小生成树,将兴建的节点看成边,原有的强连通分量看做节点,那么n个节点连成一颗生成树必然需要n-1个节点。

下面的问题就是如何染色。

如果用floodfill会很复杂,而且显然会超时。我们要考虑更加优秀的的算法。我们现在需要将在一个强连通分量中的原节点赋予同一个标识,并查集!

再加一个标记数组(n+m 表示某行或某列上是否有节点)。每读入一个节点我们就将其所在的行和列和并在一个集合中。这样就将一个强连同分量中的所有行和列合并在了同一个集合中。

最后,只需要枚举每行(列),如果该行(列)被标记过(在某个强连通分量中),那么找到它的根节点,累计总数并且标记当前强连通分量被访问过(应用并查集压缩路径后在同一个集合中的元素的祖先相同),只需标记其祖先即可。

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
program liukeke;
var
f:
array [ 1 .. 400000 ] of longint;
v:
array [ 1 .. 400000 ] of boolean;
i,n,m,p,x,y,temp1,temp2,ans:longint;

function find(x:longint):longint;
begin
if f[x] = x then exit(x);
f[x]:
= find(f[x]);
exit(f[x]);
end ;

begin
assign(input,
' wei.in ' );reset(input);
assign(output,
' wei.out ' );rewrite(output);
readln(n,m,p);
for i: = 1 to n + m do f[i]: = i;
for i: = 1 to p do
begin
readln(x,y);
temp1:
= find(x);
temp2:
= find(y + n);
if temp1 <> temp2 then
f[temp1]:
= temp2;
v[x]:
= true;
v[y
+ n]: = true;
end ;
for i: = 1 to n + m do
if v[i] then
begin
temp1:
= find(i);
if v[temp1] then
begin
inc(ans);
v[temp1]:
= false;
end ;
end ;
writeln(ans
- 1 );
close(input);
close(output);
end .

反思

要先对题目分析,抽象其模型,再选取合适的数据结构解决问题,并查集应用不多但都很巧妙,可用来判断逻辑关系和集合关系

转载于:https://www.cnblogs.com/liukeke/archive/2011/06/12/2078972.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值