东东的轰炸游戏 (DFS+并查集)

1 篇文章 0 订阅
1 篇文章 0 订阅

东东的轰炸游戏(bomb.pas/.c/.cpp)
题目描述 东东 和东东爸在玩一个即时战略游戏,东东要轰炸东东爸 的基地,其中东东爸的基地 由n个建筑物和m条道路联通。东东每次轰炸只能炸毁东东爸 基地中的一条道路。
假设有一条道路【x,y】,如果被东东炸毁后,建筑物x和y不联通,那么东东就会非 常的高兴,下次轰炸就会选择炸毁下一条道路;否则如果x 和y联通,那么东东就非常的 生气,下次轰炸会改变原来的主意,选择炸毁另外一条道路。
输入格式 第一行两个整数n,m。表示东东爸 的基地有n个建筑物,m条道路。
接下来m 行中,每i行两个整数xi,yi,表示第i号边连接建筑物xi和yi。
接下来一行一个正整数k。表示东东顺序的k次轰炸。
接下来k行中,第一行一个正整数,表示第一次轰炸的道路的编号。之后的k-1行每行 两个正整数a,b,其中如果上次轰炸东东高兴,这次选择第a号边,否则选择第b号边。
数据保证:图中没有自环、重边;每条道路最多会被轰炸一次。
输出格式 输出k行,每行对应一个结果。 如果东东高兴那么输出“:)”,否则输出“:(”。(不含引号)
样例输入
3 3
1 2
2 3
3 1
2
1
2 3
样例输出
:(
:)
数据范围与约定
1到5号测试点:n<=1000,m<=10000,k<=1000,不保证a=b。
6到10号测试点:n<=10000,m<=100000,k<=m,并且保证a=b。

这是一道分成两半的题。。 很容易看出来如果数据很水可以直接dfs爆搜,幸运的是前五个点应该设置的都是暴力点,使用邻接表记录路径,每次删边之后dfs判断能否走到即可。 至于后五个点,因为a=b所以不用考虑路径问题 ,即不需要考虑上一次轰炸的高兴与否。所以先将所有的路径存入数组,把所有不需要轰炸的路径两侧的点合并,再逆着轰炸的顺序,先判断两点是否在一个集合中,用boolean数组记录,然后合并。最后按照顺序输出结果即可

program mys;

var a,i,j,k,m,n,l,r,xx,yy:longint;
p:array[0..1000,0..7000]of longint;
b,e:array[0..200000]of boolean;
bb:array[0..150000]of boolean;
x,y:array[0..200000]of longint;
f,g:array[0..200000]of longint;

procedure dele(xx,yy:longint);
var i:longint;
begin 
for i:=1 to p[xx,0] do 
if p[xx,i]=yy then 
begin 
p[xx,i]:=0;
break;
end;
end;

procedure dfs(x:longint);
var ii:longint;
begin 
bb[x]:=true;
for ii:=1 to p[x,0] do 
begin 
if p[x,ii]=yy then 
begin 
b[i]:=true;
break;
end;
if (p[x,ii]<>0)and(bb[p[x,ii]]=false) then 
dfs(p[x,ii]);
if b[i] then break;
end;
end;

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

procedure union(x,y:longint);
begin 
x:=find(x);
y:=find(y);
if x<>y then f[x]:=y;
end;

begin
assign(input,'bomb.in'); reset(input);
assign(output,'bomb.out'); rewrite(output);
readln(n,m);

if n<=1000 then 
begin 
fillchar(b,sizeof(b),false);
for i:=1 to n do 
p[i,0]:=0;
for i:=1 to m do 
begin 
readln(x[i],y[i]);
inc(p[x[i],0]);
p[x[i],p[x[i],0]]:=y[i];
inc(p[y[i],0]);
p[y[i],p[y[i],0]]:=x[i];
end;
readln(k);
readln(a);
i:=1;
xx:=x[a]; yy:=y[a];
dele(xx,yy);
dele(yy,xx);
fillchar(bb,sizeof(bb),false);
dfs(xx);
if b[i] then writeln(':(')
else writeln(':)');
for i:=2 to k do
begin 
read(l,r);
if b[i-1]=true then
begin 
xx:=x[r]; yy:=y[r];
end
else 
begin 
xx:=x[l]; yy:=y[l];
end;
dele(xx,yy);
dele(yy,xx);
fillchar(bb,sizeof(bb),false);
dfs(xx);
if b[i] then writeln(':(')
else writeln(':)')
end;
end

else
begin 
fillchar(b,sizeof(b),true);
for i:=1 to m do 
readln(x[i],y[i]);
for i:=1 to n do 
f[i]:=i;
readln(k);
readln(g[1]);
b[g[1]]:=false;
for i:=2 to k do 
begin 
readln(g[i],g[i]);
b[g[i]]:=false;
end;
for i:=1 to m do 
if b[i] then 
union(x[i],y[i]);

for i:=k downto 1 do 
begin 
if find(x[g[i]])=find(y[g[i]]) then 
e[i]:=false
else e[i]:=true;
union(x[g[i]],y[g[i]]);
end;
for i:=1 to k do 
if e[i] then 
writeln(':)')
else writeln(':(');
end;

close(input);
close(output);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值