题目大意:
有N个村庄和M个区域,每个区域都是由村庄之间的围墙所围成(每个区域用村庄来描述,按顺时针方向给出区域周围的村庄)。有一个俱乐部的一些成员居住在村庄里,每个村庄最多居住一个成员。现在这些成员要到一个区域去开会,俱乐部成员走到该区域可能会穿墙而过,要求选定一个区域,要求这些成员的总共穿墙次数最小。
解题思路:
一个最小距离问题,关键是如何建图。可以将区域作为点,如果两个区域相邻,那么相应的点之间的权值为1(表示从区域到另一个区域,从一个点到另一个点)要穿过一堵墙。如何确定两个区域是相邻的,因为给出区域的描述的是顺时针方向上的村庄,其中一个区域的一条边(u1, v1),另一个区域的一条边(u2, v2),(都是顺时针给出的),如果u1 = v2 且 u2 = v1 那么这两个区域公用一堵墙,即两个区域相邻。还要处理第一个点和最后一个点的关系。最后一个问题,如果计算从村庄到区域经过的围墙的个数,这需要转化,将村庄到区域所经过的围墙的个数转化为区域(与村庄相邻的区域)到区域所经过的围墙的个数。选取一个最小的即为村庄到区域的信息。
代码:
#include <cstdio>
#include <cstring>
#include <vector>
#define M 300
#define INF 0x3f3f3f3f
using namespace std;
int m, ans, tmp, l;
int member[M];
vector <int> adj[M]; //保存与村庄相邻的区域
vector <int> border[M]; //保存每个区域周围的村庄的信息(顺时针保存)
int dis[M][M]; //从一个区域到另一个区域经过的围墙数目
void floyd() { //Floyd算法,求任意两点之间的距离
int i, j, k;
for(k=0; k<m; k++) {
for(i=0; i<m; i++) {
for(j=0; j<m; j++) {
if(dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
}
}
}
int main() {
int n;
int i, j, k;
while(~scanf("%d", &m)) {
scanf("%d%d", &n, &l);
for(i=0; i<l; i++) {
scanf("%d", &member[i]);
member[i] --;
}
for(i=0; i<n; i++) adj[i].clear();
for(i=0; i<m; i++) border[i].clear();
for(i=0; i<m; i++) {
for(j=0; j<m; j++)
if(i == j) dis[i][j] = 0;
else
dis[i][j] = INF;
}
int u, v, su;
for(i=0; i<m; i++) {
scanf("%d", &k);
scanf("%d", &su);
su --;
border[i].push_back(su);
adj[su].push_back(i);
u = su;
for(j=1; j<k; j++) {
scanf("%d", &v);
v --;
border[i].push_back(v);
adj[v].push_back(i);
for(int ii=0; ii<i; ii++) {
for(int jj=0; jj<border[ii].size()-1; jj++)
if(border[ii][jj] == v && border[ii][jj+1] == u ) { //注意点的顺序
dis[ii][i] = dis[i][ii] = 1;
}
if(border[ii][0] == u && border[ii][border[ii].size()-1] == v) { //还是要注意点的顺序
dis[ii][i] = dis[i][ii] = 1;
}
}
u = v;
}
v = su;
for(int ii=0; ii<i; ii++) {
for(int jj=0; jj<border[ii].size()-1; jj++)
if(border[ii][jj] == v && border[ii][jj+1] == u ) {
dis[ii][i] = dis[i][ii] = 1;
}
if(border[ii][0] ==u && border[ii][border[ii].size()-1] == v) {
dis[ii][i] = dis[i][ii] = 1;
}
}
}
floyd();
ans = INF; tmp = INF;
int th;
int tmpAns = 0;
for(i=0; i<m; i++) {
tmpAns = 0;
for(j=0; j<l; j++) {
tmp = INF;
int jj = member[j];
for(k=0; k<adj[jj].size(); k++) { //寻找与村庄相邻的最优的区域
if(dis[i][adj[jj][k]] < tmp) {
tmp = dis[i][adj[jj][k]];
th = adj[jj][k];
}
}
tmpAns += dis[i][th];
}
if(tmpAns < ans) ans = tmpAns;
}
printf("%d\n", ans);
}
return 0;
}