安全网络(SafeNet)
问题描述:
GDOI是一家跨国大型网络技术公司,主要从事局域网和广域网的开发研究工作。最近GDOI的工程师们在研究安全网络的问题。让我们来看看他们的研究成果。
一台服务器S被称为关键的,当且仅当存在另外两台服务器A和B,且A与B之间相连的网络连线必需从S通过(即如果S不幸崩溃,A和B将无法相连)。例如,在图一所示网络中,服务器1和3就是关键的。
图一 图二
一个网络被称为安全的,当且仅当其不包含关键的服务器。此外,还有如下规定:
①两台服务器之间的连接是双向的;
②服务器不会与自身直接相连;③允许网络中存在孤立的子网络。
①两台服务器之间的连接是双向的;
②服务器不会与自身直接相连;③允许网络中存在孤立的子网络。
一个子网被称为最大安全子网,当且仅当其不被其它安全子网所包含,即无法跟其它安全子网合并而形成一个更大的安全子网。图二列出了图一中所示网络的所有最大安全子网。
现在,你要到GDOI应聘,负责面试的工程师们给你留了一道难题,如何在给定的网络中找出所有最大安全子网呢?
输入格式:
输入文件第一行是一个正整数N(N≤100),代表网络中服务器的个数。
接下来是N行数据,每一行的格式如下:(各个数之间用一个或多个空格分开)
接下来是N行数据,每一行的格式如下:(各个数之间用一个或多个空格分开)
I K C
I是服务器编号,K表示与服务器I直接相连的服务器个数,紧接着的K个数字C是与服务器I直接相连的服务器的编号。
I是服务器编号,K表示与服务器I直接相连的服务器个数,紧接着的K个数字C是与服务器I直接相连的服务器的编号。
输出格式:
输出文件第一行是一个正整数M,为所求的最大安全子网的个数。接下来的M行列出每一个子网的组成情况,其中第一个数字是该子网所包含的服务器个数A,后面紧跟着A个正整数,为组成该子网的服务器的编号。(各个数之间用一个空格分开)
输入输出样例:
8
0 1 1
0 1 1
1 3 2 0 3
2 2 1 3
3 3 1 2 4
4 1 3
7 1 6
6 1 7
5 0
5
2 0 1
3 1 2 3
2 3 4
1 5
2 6 7
刚开始我因为没读懂题目敲了个强连通分量,其实实际上是割点问题。
观察样例,我们可以发现所有最大安全子网是根据关键服务器来割的,所以我们只需枚举检查出所有关键服务器并割成独立的连通分量即可。
具体说来,就是一开始存下所有连通分量,然后枚举所有服务器,对于每个服务器,检查它相邻的点能否不经过此服务器到达其它与此服务器相邻的点,若存在一组无法到达,则此服务器为关键服务器。
重点在于怎么割。
首先要找到当前关键服务器所在的连通分量,通过对相邻的所有点进行宽搜以找出所有独立的新连通分量(即这几个连通分量之间仅能通过此关键服务器相连)。
由于涉及到快速查找元素(查找关键服务器所在的连通分量),建议使用集合set。
当然,思想很简单,细节很复杂,详见代码。
本题应有special judge(然而我当时校内测试时竟然没有),另外我的数据中数据范围为200(使得我一直搞不懂程序怎么崩溃的),原样例也是错的(原样例输出第一行是4),不知道大家有没也遇到这些问题……
代码:
#include<cstdio>
#include<cstring>
#include<set>
#include<iostream>
using namespace std;
int n,top,cnt,point;
int edge[200][201];
bool key[200]; //标记当前点是否为关键服务器(注意割完后已不是关键服务器,故应恢复为false),可省略
bool vis[200]; //找连通分量时判断点是否已遍历过(被归入连通分量中)
bool used[200]; //是否被归入连通分量之中
int stack[201]; //存新找出的连通分量(可以直接用s代替)
bool avai[1001]; //是否有效(即当前已检查的所有服务器中该连通分量是否含关键服务器,若有,则无效,为true)
set<int>s[1001]; //存下所有连通分量,最终所剩有效的连通分量为最大安全子网
void find(int now)
{
used[now]=true;
stack[++top]=now;
for (int i=1;i<=edge[now][0];i++)
if (!key[edge[now][i]]&&!vis[edge[now][i]]&&s[point].find(edge[now][i])!=s[point].end())
{
vis[edge[now][i]]=true;
find(edge[now][i]);
}
}
void find_new(int now)
{
used[now]=true;
stack[++top]=now;
for (int i=1;i<=edge[now][0];i++)
if (!vis[edge[now][i]])
{
vis[edge[now][i]]=true;
find_new(edge[now][i]);
}
}
bool check(int x,int y,int t) //宽搜检查关键服务器
{
vis[x]=true;
bool p=false;
if (x==y)
return true;
for (int i=1;i<=edge[x][0];i++)
if (edge[x][i]!=t&&!vis[edge[x][i]])
return p|check(edge[x][i],y,t);
return p;
}
int main()
{
freopen("safenet.in","r",stdin);
freopen("safenet.out","w",stdout);
set<int>::iterator it;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
scanf("%d",&edge[x][0]);
for (int j=1;j<=edge[x][0];j++)
scanf("%d",&edge[x][j]);
}
for (int i=0;i<n;i++) //先找出所有的连通分量
if (!used[i])
{
cnt++;
memset(vis,false,sizeof(vis));
vis[i]=true;
top=0;
find_new(i);
for (int j=1;j<=top;j++)
s[cnt].insert(stack[j]);
}
for (int t=0;t<n;t++)
{
for (int j=1;j<edge[t][0];j++) //检查所有相邻的点是否在不经过此服务器条件下互相连通
{
for (int k=j+1;k<=edge[t][0];k++)
{
memset(vis,false,sizeof(vis));
if (!check(edge[t][j],edge[t][k],t))
{
key[t]=true;
break;
}
}
if (key[t]) break;
}
if (key[t])
{
for (int i=1;i<=cnt;i++) //查找该关键服务器位于哪个连通分量
if (!avai[i])
if (s[i].find(t)!=s[i].end()) //判断点是否在连通分量内
{
avai[i]=true; //含关键服务器,应判为无效且进行割点
point=i;
for (it=s[i].begin();it!=s[i].end();it++)
used[*it]=false;
break;
}
for (int j=1;j<=edge[t][0];j++) //割点
if (!used[edge[t][j]])
{
memset(vis,false,sizeof(vis));
vis[edge[t][j]]=true;
top=0;
find(edge[t][j]);
cnt++;
for (int i=1;i<=top;i++)
s[cnt].insert(stack[i]);
s[cnt].insert(t);
}
used[t]=true;
}
key[t]=false;
}
int tot=0;
for (int i=1;i<=cnt;i++)
if (!avai[i])
tot++;
printf("%d\n",tot);
for (int i=1;i<=cnt;i++)
if (!avai[i])
{
printf("%d ",s[i].size());
for (it=s[i].begin();it!=s[i].end();it++)
printf("%d ",*it);
printf("\n");
}
return 0;
}
另外附上两个可用的special judge(一个是我写的,一个是我同学写的):
uses
libcheck; //注意使用时要将Cena的libcheck文件复制到同一文件夹下
var
n,i,j,k,cnt,nstd,len:longint;
a:array[1..200,0..200]of longint;
astd:array[1..200,0..200]of longint;
used:array[1..200]of boolean;
p,t:boolean;
procedure wrong;
begin
writeln(rep,'Wrong Answer!');
score(0);
finish;
halt;
end;
procedure swap(var a,b:longint);
var
k:longint;
begin
k:=a;
a:=b;
b:=k;
end;
procedure asort(l,r,now:longint);
var
i,j,m:longint;
begin
i:=l;
j:=r;
m:=a[now,(l+r) div 2];
repeat
while a[now,i]<m do inc(i);
while a[now,j]>m do dec(j);
if i<=j then
begin
swap(a[now,i],a[now,j]);
inc(i);
dec(j);
end;
until i>j;
if i<r then asort(i,r,now);
if j>l then asort(l,j,now);
end;
procedure astdsort(l,r,now:longint);
var
i,j,m:longint;
begin
i:=l;
j:=r;
m:=astd[now,(l+r) div 2];
repeat
while astd[now,i]<m do inc(i);
while astd[now,j]>m do dec(j);
if i<=j then
begin
swap(astd[now,i],astd[now,j]);
inc(i);
dec(j);
end;
until i>j;
if i<r then astdsort(i,r,now);
if j>l then astdsort(l,j,now);
end;
begin
assign(input,'safenet.out');
reset(input);
readln(n);
while not eof do
begin
inc(cnt);
read(a[cnt][0]);
for i:=1 to a[cnt,0] do
read(a[cnt,i]);
asort(1,a[cnt,0],cnt);
readln;
end;
if cnt<>n then wrong;
close(input);
readln(std,nstd);
if nstd<>n then wrong;
for i:=1 to nstd do
begin
read(std,astd[i,0]);
for j:=1 to astd[i,0] do
read(std,astd[i,j]);
astdsort(1,astd[i,0],i);
end;
for i:=1 to n do
begin
t:=false;
for j:=1 to n do
if not used[j] then
begin
p:=false;
if a[i,0]<>astd[j,0] then continue;
len:=a[i,0];
for k:=1 to len do
if a[i,k]<>astd[j,k] then
begin
p:=true;
break;
end;
if not p then
begin
used[j]:=true;
t:=true;
break;
end;
end;
if not t then wrong;
end;
score(fsco);
finish;
end.
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#include <vector>
#include <deque>
#include <set>
#include <limits>
#include <string>
#include <sstream>
using namespace std;
namespace dts
{
string maxs;
string outfilen="safenet.out";
string instdn="safenet.in";
string outstdn;
string temp;
FILE *outfile,*instd,*outstd;
int n,na,nb;
int asize[1000+1];
int bsize[1000+1];
int a[1000+1][1000+1];
int b[1000+1][1000+1];
int cmp(int a,int b)
{
return a<b;
}
int check(int *a,int *b,int ax,int bx)
{
if (asize[ax]!=bsize[bx])
return 0;
else
{
int size=asize[ax]=bsize[bx];
for (int i=1;i<=size;i++)
if (a[i]!=b[i])
return 0;
return 1;
}
}
void wrong()
{
freopen("score.log","w",stdout);
printf("0\n");
exit(0);
}
void accept()
{
freopen("score.log","w",stdout);
printf("%s\n",maxs.c_str());
exit(0);
}
void report(string s)
{
freopen("report.log","w",stdout);
printf("%s\n",s.c_str());
exit(0);
}
void main(int argc,char *argv[])
{
maxs=argv[1];
outstdn=argv[2];
outfile=fopen(outfilen.c_str(),"r");
instd=fopen(instdn.c_str(),"r");
outstd=fopen(outstdn.c_str(),"r");
fscanf(outfile,"%d",&na);
fscanf(outstd,"%d",&nb);
if (na==nb)
{
n=na=nb;
for (int i=1;i<=n;i++)
{
fscanf(outfile,"%d",&asize[i]);
for (int j=1;j<=asize[i];j++)
fscanf(outfile,"%d",&a[i][j]);
sort(&a[i][1],&a[i][asize[i]+1],cmp);
fscanf(outstd,"%d",&bsize[i]);
for (int j=1;j<=bsize[i];j++)
fscanf(outstd,"%d",&b[i][j]);
sort(&b[i][1],&b[i][bsize[i]+1],cmp);
}
for (int i=1;i<=n;i++)
for (int j=1;j<i;j++)
if (check(a[i],a[j],i,j))
wrong();
for (int i=1;i<=n;i++)
{
int flag=0;
for (int j=1;j<=n;j++)
if (check(a[i],b[j],i,j))
{
flag=1;
break;
}
if (flag==0)
wrong();
}
accept();
}
else
wrong();
}
};
int main(int argc,char *argv[])
{
dts::main(argc,argv);
}