题意:有n个铁环用长度均为1的绳子连起来(不会有重边)。试图拿起两个铁环并在水平方向拉直,那么会有一些边会被拉直(可想而知有些绳子不会被拉直而是弯曲的),那么取哪两个铁环并拉直,能使被拉直的直线最多呢?输出最多的边数
这题其实是最短路,而且通过这题可以帮助我们更形象地理解最短路的实质尤其理解松弛操作
当两个点a,b被拉直,那么其实那些被拉直的边就是最短路径(从a出发到b,或者反过来也行,因为是无向图),也就是说,要找到所有的最短路径并统计有多少条不同的边(因为不同的最短路径可能共用相同的边),那么就是这两点的数目,我们只要枚举任意两个点被拉直时不同边的数目,并找到最大的那个就可以了。
所以要先知道任意两个点的最短路,floyd,然后就是dfs搜索所有可能的边,dfs搜索的时候直接使用floyd之后的d数组,并且这个我写的这个dfs感觉有点像DP记忆化搜索,因为当初想这个问题的时候就想到找不同的边用DP去做,但没想出来,然后YY了一个DFS,WA了几次终于是过,才发现挺像记忆化搜索的
直接看代码的注释吧
#include <cstdio> #include <cstring> #define INF 0x3f3f3f3f #define N 130 int d[N][N],vis[N][N]; int n,m,ccount,max; void input() { scanf("%d%d",&n,&m); memset(d,0x3f,sizeof(d)); for(int i=1; i<=m; i++) { int u,v; scanf("%d%d",&u,&v); d[u][v]=d[v][u]=1; } return ; } void floyd() { for(int k=0; k<n; k++) for(int i=0; i<n; i++) for(int j=0; j<n; j++) { if(d[i][k]+d[k][j]<d[i][j]) d[i][j]=d[i][k]+d[k][j]; } for(int i=0; i<n; i++) d[i][i]=0; return ; } void dfs(int sp,int len,int t ,int u) //sp为最短路值,len为当前找到的长度,t是终点,u是当前的点 { if(u==t) return ; for(int v=0; v<n; v++) if(v!=u && d[u][v]==1 && !vis[u][v]) if(len+d[u][v]+d[v][t]==sp) { ccount++; vis[u][v]=vis[v][u]=1; dfs(sp,len+d[u][v],t,v); } return ; } void solve() { max=0; for(int s=0; s<n; s++) //枚举所有的起点和终点 for(int t=s+1; t<n; t++) //因为是无向图,所以j=s+1开始 { memset(vis,0,sizeof(vis)); ccount=0; if(d[s][t]==1) //某些最短路直接相连为1,不用搜索直接判断 { if(max<1) max=1; continue; } dfs(d[s][t],0,t,s); //printf("顶点%d %d:%d\n",s,t,ccount); if(ccount>max) max=ccount; } } void print_graph() { for(int i=0; i<n; i++) { for(int j=0; j<n; j++) printf("%d ",d[i][j]); printf("\n"); } } int main() { int T; scanf("%d",&T); for(int c=1; c<=T; c++) { input(); //print_graph(); floyd(); //print_graph(); solve(); printf("Case #%d: %d\n",c,max); } return 0; }