VIJOS-P1321 魔塔
Time Limit: 1 Sec Memory Limit: 128 MBDescription
在魔塔中有N个房间和M条道路,每条道路上有一个怪,它可以被一种特殊的武器消灭,而每个房间中也存在一种武器。现在知道第I个房间中的武器编号为I,小明(主人翁)初始在J房间,小明想知道哪些房间是他可以去的。
Input
第一行是N,J,M 接下来M行每行三个数Ai,Bi,Ci,分别代表Ai房间和Bi房间之间有路,且此处的怪物可以被Ci号武器消灭。
Output
N行,如果I个房间可以到达,则在第I行输出Yes,否则输出No
Sample Input
6 4 6
1 2 1
1 3 2
2 4 4
3 4 4
3 5 3
5 6 6
Sample Output
1:Yes
2:Yes
3:Yes
4:Yes
5:Yes
6:No
HINT
数据范围 1< =m< =50000,1< =a,b,J< =n< =50000 提示:m,n< 50000不等于说数组可以只开到50000;输出前面无空格
Source
中文题我为什么要告诉你题意?好的,题意就是这样。
现在我要说题解!
首先wyf写了一个暴力,90分,我贴一下他的博客,说不定他写了呢~http://blog.csdn.net/wyfcyx_forever
然后我要说我的正解了。
思想:
每次将能走的点跑一遍确定能开的边,这样就拓展了能走的点,重复操作以得出答案。
实现:
就是先把每个节点用链表挂上其能开的边,然后维护并查集和树构还有一个栈。
并查集:一个点的父亲节点,用于判断连通(/与起点连通)
树构:一棵有向树,每次搜索时从树根扫,然后就可以O(节点数)扫过全部点,把能新开的边计入。
栈:每次当有新开的边将某并查集与起点连通时,把此并查集(树构)的根节点入栈。
具体实现可以参考代码,有新鲜出炉的注释。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 50500
#define inf 0x3f3f3f3f
using namespace std;
struct KSD
{
int u,v,next;
}e[N],ke[N<<1];
int head[N],key[N],cnt,cdk;
void add(int u,int v)
{/*树构*/
cnt++;
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void open(int u,int v,int ky)
{/*给每个点挂上能开的边*/
cdk++;
ke[cdk].u=u;
ke[cdk].v=v;
ke[cdk].next=key[ky];
key[ky]=cdk;
}
int f[N];/*并查集*/
int find(int x)
{
if(x==f[x])return x;
return f[x]=find(f[x]);
}
int n,m,s;
int stk[N],top;/*栈*/
bool visit[N];
void dfs(int x)
{
visit[x]=1;
int i,u,v;
int fa,fb;
for(i=key[x];i;i=ke[i].next)
{/*把x节点能开的边(门)都打开*/
fa=find(ke[i].u);
fb=find(ke[i].v);
if(fa==fb)continue;/*额,你懂得*/
if(fa==s)stk[++top]=fb,f[fb]=s;
else if(fb==s)stk[++top]=fa,f[fa]=s;
/*
某节点被连到了起点,表示能走,
那么就需要处理这个并查集中所有的节点,
把它们能开的边(门)都打开,
这时我们就需要
1. 一个树构来实现高速遍历
2. 一个链表来实现查找边。
*/
else
{/*额,没关系的就自己弄个树构先存着吧*/
add(fa,fb);
f[fb]=fa;
}
}
if(x!=s)for(i=head[x];i;i=e[i].next)
{/*遍历树构中其它点*/
v=e[i].v;
dfs(v);
}
}
int main()
{
// freopen("test.in","r",stdin);
int i,j,k;
int a,b,c;
int u,v,t;
scanf("%d%d%d",&n,&s,&m);
for(i=1;i<=n;i++)f[i]=i;
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
open(a,b,c);
}
stk[++top]=s;
while(top){dfs(stk[top--]);}
/*
栈中是从起点这个树根能遍历到的一些枝杈,
它们本身也是一颗颗树
而遍历过程中会有新的边变成可行,
这样就会不停地加进来新的点/点集,
或者说是树。
而!top时遍历也就停止了,
额,没看懂的话不妨手调。
*/
for(i=1;i<=n;i++)
{
printf("%d:",i);
if(visit[i])puts("Yes");
else puts("No");
}
return 0;
}

博客详细介绍了Vijos P1321题目的解决方案,使用并查集和树形结构进行非暴力求解。通过维护链表连接可到达的点,构建有向树,并利用栈来优化搜索过程,最终达到高效求解的目的。代码中包含详细的注释。

1210

被折叠的 条评论
为什么被折叠?



