作者在 2010-12-30 00:19:34 发布以下内容
Description
现在有个一个内部局域网络,里面有N台机器。为了某种安全原因的考虑,有些机器之间是无法直接通讯的,即使可以通讯,机器与机器之间的通讯都是经过加密的。由于不同机器之间传输的内容不同,所以他们通讯采用的加密级别也不大相同。不同的加密级别导致破解的难度不一样,越高的加密级别破解需要的时间也越多。如果我们获得了编号为i的机器的完全控制权,且机器i和机器j可以直接通讯,另外我们破解了机器i和机器j之间的加密信息,那么我们就得到了机器j的完全控制权。
现在你通过了某种手段入侵了1号机器,得到了这台机器的完全控制权,为了扩大劳动果实,你准备对其余的机器也在你的控制当中,但是由于需要破解加密信息才能控制其它机器,你又不想浪费太多时间在破解上,现在你来算算你至少需要多少时间才能完全控制整个网络。
Input
输入的第一行是两个正整数N(2 <= N <= 100,000) M(0 < M <= 200,000),表示机器的数目和允许通讯的机器对数。
输入的第二行开始到第M+1行,每行3个整数,A B T( 1 <= A, B <= N, T <= 10,000, A ≠ B),表示机器A和机器B之间可以互相通讯,且破解这个通讯的时间是T。输入保证不存在重复的AB对。
Output
输出完全控制所有机器的最少时间。如果无法满足要求则输出-1。
Sample Input
4 4
1 2 4
1 3 9
2 3 2
4 3 1
Sample Output
7
由于n=100,000,O(n2)的算法必定超时,我们知道当图为稀疏图时,最小生成树的Kruskal O(mlogm)算法,比传统的Prim()要快的多;
在实现Kruskal算法是,涉及到并查集的两个操作,Find( ), Union( ),应该都很了解了,具体的实现中用到 路径压缩和按秩合并 以提高运行效率;
源程序:
#include <iostream>
using namespace std;
struct Node{
int rank;
struct Node *p;
};
struct Edge{
int u;
int v;
int len;
};
Edge edge[200001];
Node node[100001];
int n,m;
Node *Find(Node *xp) //查找节点所属的集合,用到路径压缩
{
Node *yp=xp;
Node *zp=xp;
Node *wp;
while(yp->p!=NULL)
yp=yp->p;
while(zp->p!=NULL)
{
wp=zp->p;
zp->p=yp;
zp=wp;
}
return yp;
}
void Union(Node *up, Node *vp) //合并两个集合,用到按帙合并
{
if(up->rank>=vp->rank)
{
vp->p=up;
if(up->rank==vp->rank)
up->rank++;
}
else
{
up->p=vp;
}
}
void swap(Edge &edge1, Edge &edge2)
{
Edge tmp=edge1;
edge1=edge2;
edge2=tmp;
}
int partion(int p, int q)
{
int t=rand()%(q-p+1)+p;
swap(edge[t],edge[p]);
int i=p;
int j=q+1;
while(true)
{
while(edge[++i].len<edge[p].len);
while(edge[--j].len>edge[p].len);
if(i>=j)break;
swap(edge[i],edge[j]);
}
swap(edge[p],edge[j]);
return j;
}
void quicksort(int p, int q)
{
if(p<q)
{
int r=partion(p,q);
quicksort(p,r-1);
quicksort(r+1,q);
}
}
void Kruskal()
{
int i,c,len;
Node *uFind,*vFind;
quicksort(1,m);
c=len=0;
for(i=1;i<=m;i++)
{
uFind=Find(&node[edge[i].u]);
vFind=Find(&node[edge[i].v]);
if(uFind!=vFind)
{
c++;
len+=edge[i].len;
if(c==n-1)
{
printf("%d\n",len);
return;
}
Union(uFind,vFind);
}
}
printf("-1\n"); //网络不连通
}
int main()
{
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].u, &edge[i].v, &edge[i].len);
Kruskal();
return 0;
}
现在有个一个内部局域网络,里面有N台机器。为了某种安全原因的考虑,有些机器之间是无法直接通讯的,即使可以通讯,机器与机器之间的通讯都是经过加密的。由于不同机器之间传输的内容不同,所以他们通讯采用的加密级别也不大相同。不同的加密级别导致破解的难度不一样,越高的加密级别破解需要的时间也越多。如果我们获得了编号为i的机器的完全控制权,且机器i和机器j可以直接通讯,另外我们破解了机器i和机器j之间的加密信息,那么我们就得到了机器j的完全控制权。
现在你通过了某种手段入侵了1号机器,得到了这台机器的完全控制权,为了扩大劳动果实,你准备对其余的机器也在你的控制当中,但是由于需要破解加密信息才能控制其它机器,你又不想浪费太多时间在破解上,现在你来算算你至少需要多少时间才能完全控制整个网络。
Input
输入的第一行是两个正整数N(2 <= N <= 100,000) M(0 < M <= 200,000),表示机器的数目和允许通讯的机器对数。
输入的第二行开始到第M+1行,每行3个整数,A B T( 1 <= A, B <= N, T <= 10,000, A ≠ B),表示机器A和机器B之间可以互相通讯,且破解这个通讯的时间是T。输入保证不存在重复的AB对。
Output
输出完全控制所有机器的最少时间。如果无法满足要求则输出-1。
Sample Input
4 4
1 2 4
1 3 9
2 3 2
4 3 1
Sample Output
7
由于n=100,000,O(n2)的算法必定超时,我们知道当图为稀疏图时,最小生成树的Kruskal O(mlogm)算法,比传统的Prim()要快的多;
在实现Kruskal算法是,涉及到并查集的两个操作,Find( ), Union( ),应该都很了解了,具体的实现中用到 路径压缩和按秩合并 以提高运行效率;
源程序:
#include <iostream>
using namespace std;
struct Node{
int rank;
struct Node *p;
};
struct Edge{
int u;
int v;
int len;
};
Edge edge[200001];
Node node[100001];
int n,m;
Node *Find(Node *xp) //查找节点所属的集合,用到路径压缩
{
Node *yp=xp;
Node *zp=xp;
Node *wp;
while(yp->p!=NULL)
yp=yp->p;
while(zp->p!=NULL)
{
wp=zp->p;
zp->p=yp;
zp=wp;
}
return yp;
}
void Union(Node *up, Node *vp) //合并两个集合,用到按帙合并
{
if(up->rank>=vp->rank)
{
vp->p=up;
if(up->rank==vp->rank)
up->rank++;
}
else
{
up->p=vp;
}
}
void swap(Edge &edge1, Edge &edge2)
{
Edge tmp=edge1;
edge1=edge2;
edge2=tmp;
}
int partion(int p, int q)
{
int t=rand()%(q-p+1)+p;
swap(edge[t],edge[p]);
int i=p;
int j=q+1;
while(true)
{
while(edge[++i].len<edge[p].len);
while(edge[--j].len>edge[p].len);
if(i>=j)break;
swap(edge[i],edge[j]);
}
swap(edge[p],edge[j]);
return j;
}
void quicksort(int p, int q)
{
if(p<q)
{
int r=partion(p,q);
quicksort(p,r-1);
quicksort(r+1,q);
}
}
void Kruskal()
{
int i,c,len;
Node *uFind,*vFind;
quicksort(1,m);
c=len=0;
for(i=1;i<=m;i++)
{
uFind=Find(&node[edge[i].u]);
vFind=Find(&node[edge[i].v]);
if(uFind!=vFind)
{
c++;
len+=edge[i].len;
if(c==n-1)
{
printf("%d\n",len);
return;
}
Union(uFind,vFind);
}
}
printf("-1\n"); //网络不连通
}
int main()
{
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].u, &edge[i].v, &edge[i].len);
Kruskal();
return 0;
}