题意:给出n个点和l个区域(这些区域就是图中的面)。输入前三行为r,n,l,分别表示区域数、点数、和俱乐部数。第四行有l个数,分别标记哪些个点是俱乐部。接下来2*r行,代表r个区域,每个区域由两行表示,第一行为区域由num个点围成的,第二行num个数代表是哪些点围成这个区域,这些点按顺时针(最后一个表示最外区域时是逆时针)围成这个区域,相邻两点代表一条边。问,在图中找出一个区域,使得所有俱乐部点到这个区域所要穿过的边之和最少,输出最少的边数之和。
思路:实际上就是暴搜。先以面之间是否直连构图(注意flag的应用)。然后枚举每个club点,算其与所有面的最短距离,实际上就是权值都为1的最短路,bfs即可(必须穿过的边数)。最后扫一遍r个区域求最小值即可。这样做的复杂度是O(nr)。(其他人有的做法是通过上面构图求Floyd最短路,然后再找没有必要)。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 255
#define R 205
int c[35],g[R][R],flag[N][N],dis[35][R],adj[N][R],used[R];
int r,n,l;
void bfs(int j){
int i,now,front,rear,q[N],x;
x = c[j];
front = rear = -1;
memset(used, 0, sizeof(used));
for(i = 1;i<=r;i++)
if(adj[x][i]){
dis[j][i] = 0;
used[i] = 1;
q[++rear] = i;
}
while(front < rear){
now = q[++front];
for(i = 1;i<=r;i++)
if(g[now][i] && !used[i]){
used[i] = 1;
q[++rear] = i;
dis[j][i] = dis[j][now]+1;
}
}
}
int main(){
int i,j,num,a,b=0,tmp,res=0x3fffffff;
memset(flag, 0, sizeof(flag));
memset(dis, 0, sizeof(dis));
memset(adj, 0, sizeof(adj));
memset(g, 0, sizeof(g));
scanf("%d %d %d",&r,&n,&l);
for(i = 1;i<=l;i++)
scanf("%d",&c[i]);
for(i = 1;i<=r;i++){
scanf("%d",&num);
scanf("%d",&a);
adj[a][i] = 1;
tmp = a;
for(j = 1;j<num;j++){
scanf("%d",&b);
adj[b][i] = 1;
if(!flag[a][b])
flag[a][b] = flag[b][a] = i;
else
g[i][flag[a][b]] = g[flag[a][b]][i] = 1;
a = b;
}
if(num>=3){
if(!flag[tmp][b])
flag[tmp][b] = flag[b][tmp] = i;
else
g[i][flag[tmp][b]] = g[flag[tmp][b]][i] = 1;
}
}
for(i = 1;i<=l;i++)
bfs(i);
for(i = 1;i<=r;i++){
for(j = 1,tmp = 0;j<=l;j++)
tmp += dis[j][i];
res = min(res,tmp);
}
printf("%d\n",res);
return 0;
}