题意是:
给出n个点,围成m个区域。从区域到另一个区域间需穿过至少一条边(若两区域相邻)——边连接着两点。
给出这么一幅图,并给出一些点,问从这些点到同一个区域的穿过边数最小值。
解题思路如下:
将区域按1~m编号,d[i][j]表示第 i 个区域到第 j 个区域的最短距离,跑一遍Floye算法O(m^3)后,枚举选择的区域,找出其中穿过边数最小值即可。
建图:题目对于每个区域的描述方式为以顺时针方向给出围成区域的点。由此可知区域由哪些边组成。易知,每条边能且只能令两个区域相邻,则用二维数组记录当前边结点所围成的区域编号,如vis[i][j]表示结点 i 和结点 j 的之间的边参与围成的区域编号。开始时memset(vis ,0,sizeof(vis)); 若是0,则将其赋值为区域编号;否则得到了相邻区域的编号,赋值d[i][vis[u][v]]=1,则初始化完成。
注意给vis数组赋值时需要vis[i][j]=vis[j][i]=区域编号。
枚举时,因为是计算从某些点到同一区域的最小距离和,而d[][]记录的是区域与区域之间的和,因此以下两点:
1. 对d[i][i]必须赋值为0,这题会用到
2. 为了方便枚举,用belong数组来记录所有点所属的区域。因此求该点到某一区域的最小距离转化成了求该点所有所属区域中到某一区域的最小距离,直接d[i][j]即可。
代码如下
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 #define inf 1<<29 8 int n,m,l,num,town,d[305][305],club[35],vis[305][305]; 9 vector<int> region[205],belong[305]; 10 void Floyd(){ 11 for(int k=1;k<=m;k++) 12 for(int i=1;i<=m;i++) 13 for(int j=1;j<=m;j++) if(i!=j) 14 d[i][j]=min(d[i][j],d[i][k]+d[k][j]); 15 } 16 void ini(){ 17 for(int i=1;i<=n;i++){ 18 belong[i].clear(); 19 for(int j=1;j<=n;j++){ 20 if(i==j) d[i][j]=0; 21 else d[i][j]=inf; 22 } 23 } 24 memset(vis,0,sizeof(vis)); 25 for(int i=0;i<=m;i++) 26 region[i].clear(); 27 } 28 int main(){ 29 //freopen("in.txt","r",stdin); 30 while(~scanf("%d%d",&m,&n)){ 31 ini(); 32 scanf("%d",&l); 33 for(int i=0;i<l;i++) 34 scanf("%d",&club[i]); 35 for(int i=1;i<=m;i++){ 36 scanf("%d",&num); 37 for(int j=0;j<num;j++){ 38 scanf("%d",&town); 39 belong[town].push_back(i); 40 region[i].push_back(town); 41 } 42 region[i].push_back(region[i][0]); 43 for(int j=0;j<num;j++){//有公共边即为相邻,赋值1 44 int u=region[i][j],v=region[i][j+1]; 45 if(vis[u][v]) d[vis[u][v]][i]=d[i][vis[u][v]]=1; 46 else vis[u][v]=vis[v][u]=i; 47 } 48 } 49 Floyd(); 50 int dis,ans=inf; 51 for(int i=1;i<=m;i++){ 52 dis=0; 53 for(int j=0;j<l;j++){ 54 int tmp=inf,c=club[j]; 55 for(int k=0;k<belong[c].size();k++) 56 tmp=min(tmp,d[i][belong[c][k]]); 57 dis+=tmp; 58 } 59 ans=min(ans,dis); 60 } 61 printf("%d\n",ans); 62 } 63 return 0; 64 }
时间: 2024-11-11 15:30:29