想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
转载请注明出处:http://blog.csdn.net/wangjian8006
题目大意:给出n个点,在这些点中有些点是俱乐部点,并且有m个区域是由点围成的
输入第一行代表n个点
第二行代表m个区域
第3行代表俱乐部有L个
第四行有L个数,分别标记哪些个点事是俱乐部
接下来2*m行,代表m个区域,每个区域由两行表示,第一行为区域由T个点围成的,
第二行T个数代表是哪些点围成这个区域,这些点按逆时针围成这个区域,相邻两点代表一条边
最后一点和第一点也是一条边,这样就是一个闭合的区域
现在问,在图中找出一个区域,使得所有俱乐部点到这个区域所要穿过的边之和最少,
输出最少的边数之和
解题思路:先建图建错了,很尴尬的重新建图,将所有区域看成点建图,然后接下来的做法可以看代码注释
写完这题已经天黑了,被这题虐了一下午。...
/* floyd Memory 788K Time 63MS */ #include <iostream> using namespace std; #define MAXE 205 #define MAXV 255 #define INF INT_MAX #define min(a,b) (a>b?b:a) int n,m; int club[35],clubsum; //俱乐部情况 int regsum,firstv,secondv,tmpv,tmpedge; //用来处理区域 int map[MAXE][MAXE]; //首先保存区域相邻情况,然后保存最短路径 int edge[MAXV][MAXV]; //看边<i,j>在哪个区域,一条边只能是包含两个区域 int markedge[MAXV][MAXE]; //markedge[i][j]标记i点与j区域是否相邻 int ans; void floyd(){ int k,i,j; for(i=0;i<m;i++) map[i][i]=0; //这里注意初始化 for(k=0;k<m;k++) for(i=0;i<m;i++) if(map[i][k]!=-1){ for(j=0;j<m;j++) if(map[k][j]!=-1){ //如果i到j走不通,直接赋值 if(map[i][j]==-1 || map[i][k]+map[k][j]<map[i][j]) map[i][j]=map[i][k]+map[k][j]; } } } int main(){ #ifndef U freopen("d:\\test.in","r",stdin); #endif int i,j,k; int tmpv,tmpedge; while(~scanf("%d",&m)){ scanf("%d%d",&n,&clubsum); for(i=0;i<clubsum;i++) scanf("%d",&club[i]); memset(map,-1,sizeof(map)); memset(markedge,0,sizeof(markedge)); memset(edge,-1,sizeof(edge)); for(i=0;i<m;i++){ scanf("%d%d",®sum,&firstv); tmpv=firstv; markedge[firstv][i]=1; for(j=1;j<regsum;j++){ scanf("%d",&secondv); markedge[secondv][i]=1; //标记点和区域相邻 tmpedge=edge[tmpv][secondv]; if(tmpedge!=-1){ //一条边只被两个区域包含 map[tmpedge][i]=map[i][tmpedge]=1; //这两个区域相邻了 }else{ edge[tmpv][secondv]=edge[secondv][tmpv]=i; } tmpv=secondv; } tmpedge=edge[tmpv][firstv]; if(tmpedge!=-1){ map[tmpedge][i]=map[i][tmpedge]=1; }else{ edge[tmpv][firstv]=edge[firstv][tmpv]=i; } } //得到相邻区域的图后求最短路径 floyd(); ans=INF; for(i=0;i<m;i++){ int sum=0; for(j=0;j<clubsum;j++){ //找出和俱乐部点相邻的区域,在这些区域到i区域的一个最小值之和与答案比较 tmpv=INF; for(k=0;k<m;k++) if(markedge[club[j]][k]){ tmpv=min(tmpv,map[k][i]); } sum+=tmpv; } ans=min(ans,sum); } printf("%d\n",ans); } return 0; }
=================================================================
/* 前面错误的代码留着做个纪念吧 */ #include <iostream> #include <queue> using namespace std; #define MAXV 260 #define MAXR 210 #define INF INT_MAX #define min(a,b) (a>b?b:a) int map[MAXV][MAXV]; //存边的连通情况 int ans[MAXV][MAXR]; //存点到地区最少要穿过几条线 int m,n; //地区与点的总数 int club[40],clubsum; //俱乐部的情况 int vsum[MAXR],ver[MAXR][MAXV]; //存地区情况 int flag[MAXR]; //存边界情况,flag标记地区是否为边界地区 int d[MAXV][MAXV]; //从点到其他点最少经过几条边 void bfs(int first){ queue <int>q; bool vis[MAXV]; int i,v; memset(vis,0,sizeof(vis)); q.push(first); vis[first]=1; while(!q.empty()){ v=q.front();q.pop(); for(i=1;i<=n;i++) if(!vis[i] && map[v][i]){ q.push(i); vis[i]=1; d[first][i]=d[first][v]+1; } } } int main(){ #ifndef U // freopen("d:\\test.in","r",stdin); #endif int i,j,k; int key,tmp,vtmp; while(~scanf("%d",&m)){ scanf("%d%d",&n,&clubsum); for(i=0;i<clubsum;i++) scanf("%d",&club[i]); //输入俱乐部情况 tmp=-1; memset(map,0,sizeof(map)); //输入地区情况,并且连边 for(i=0;i<m;i++){ scanf("%d%d",&vsum[i],&ver[i][0]); for(j=1;j<vsum[i];j++) { scanf("%d",&ver[i][j]); map[ver[i][j-1]][ver[i][j]]=map[ver[i][j]][ver[i][j-1]]=1; } map[ver[i][j-1]][ver[i][0]]=map[ver[i][0]][ver[i][j-1]]=1; if(vsum[i]>tmp){tmp=vsum[i];vtmp=i;} } memset(flag,0,sizeof(flag)); for(i=0;i<tmp;i++){ for(j=0;j<m;j++) if(!flag[j]){ for(k=0;k<vsum[j];k++){ if(ver[vtmp][i]==ver[j][k]) break; } if(k<vsum[j]) flag[j]=1; } } memset(d,0,sizeof(d)); for(i=1;i<=n;i++) bfs(i); for(i=1;i<=n;i++){ for(j=0;j<m;j++){ ans[i][j]=d[i][ver[j][0]]; for(k=1;k<vsum[j];k++){ ans[i][j]=min(ans[i][j],d[i][ver[j][k]]); } } } for(i=0;i<tmp;i++){ for(j=0;j<m;j++) if(flag[j]) ans[ver[vtmp][i]][j]=min(ans[ver[vtmp][i]][j],1); } key=INF; for(i=0;i<m;i++){ tmp=0; for(j=0;j<clubsum;j++){ tmp+=ans[club[j]][i]; } key=min(key,tmp); } printf("%d\n",key); } return 0; }