2570题意:有若干家公司(每个公司用一个小写字母表示,所以公司的数量不超过26),他们分别在若干个城市的路由器间铺设了自己的光纤。给定这个城市的路由器铺设方案,现在多组查询(A,B):如果要从路由器A到路由器B传送资料,可以使用哪家公司铺设的光纤?如能则分别输出这些公司的代号。
思路:大方向上的思路是首先求出每个公司的传递闭包,然后对于查询直接查找即可。传递闭包的求法可以使用floyd,这样得做26次floyd,有点难以承受。但是这道题可以巧妙地使用位运算来简化计算。dis[i][j]的最右边一位表示从i到j用a公司能否通过,以此类推。更新的时候用这个表达式即可:dis[i][j] |= (dis[i][k] &dis[k][j]);(表示如果i到k和k到j都有路径(26家公司就一起计算了),那么i到j就一定有路径)
#include <cstdio> #include <queue> #include <algorithm> #include <cstring> using namespace std; #define INF 0x3fffffff #define clr(s,t) memset(s,t,sizeof(s)); #define N 205 int dis[N][N],n; char s[30]; int main(){ while(scanf("%d",&n) && n){ int i,j,k,a,b; clr(dis, 0); while(scanf("%d %d ",&a,&b) && (a+b)){ scanf("%s",s); for(i = 0;s[i];i++) dis[a][b] |= 1<<(s[i]-'a'); } for(k = 1;k<=n;k++) for(i = 1;i<=n;i++) for(j = 1;j<=n;j++) dis[i][j] |= (dis[i][k] & dis[k][j]); while(scanf("%d %d",&a,&b) && (a+b)){ j = k = 0; for(i = 0,j = 1;i<26;i++){ if(dis[a][b] & j){ k = 1; putchar('a'+i); } j <<= 1; } if(!k) putchar('-'); printf("\n"); } printf("\n"); } return 0; }
3275题意:FJ想按照奶牛产奶的能力给她们排序。现在已知有N头奶牛(1 ≤ N ≤ 1,000)。FJ通过比较,已经知道了M(1 ≤ M ≤ 10,000)对相对关系。每一对关系表示为“X Y”,意指X的产奶能力强于Y。现在FJ想要知道,他至少还要调查多少对关系才能完成整个排序。
思路:N头奶牛一共有C(N, 2) = N * (N - 1) / 2对关系。由现在已知的关系如能确认K对关系,则要调查的次数就是C(N, 2) - K。这很容易理解,最坏情况下调查完一对关系之后只能确定一对关系。剩下的就变成求现有图的传递闭包,算出传递闭包中已经有的边,用总数减去。
针对这道题用floyd求传递闭包有个优化,就是内层循环枚举i、j的时候只需要从k的入边中枚举i,从出边中枚举j。
#include <cstdio> #include <queue> #include <algorithm> #include <cstring> using namespace std; #define INF 0x3fffffff #define clr(s,t) memset(s,t,sizeof(s)); #define N 1005 int s[N][N],t[N][N],dis[N][N]; int n,m; int main(){ int i,j,k,a,b,res=0; scanf("%d %d",&n,&m); for(i = 1;i<=n;i++) s[i][0] = t[i][0] = 0;//s[x][0]存放出边的数量,t[x][0]存放入边的数量 clr(dis, 0); for(i = 1;i<=m;i++){ scanf("%d %d",&a,&b); dis[a][b] = 1; s[a][++s[a][0]] = b; t[b][++t[b][0]] = a; } for(k = 1;k<=n;k++){ for(i = 1;i<=t[k][0];i++){ a = t[k][i]; for(j = 1;j<=s[k][0];j++){ b = s[k][j]; if(dis[a][k]+dis[k][b]==2 && !dis[a][b]){ dis[a][b] = 1; s[a][++s[a][0]] = b; t[b][++t[b][0]] = a; } } } } res = n*(n-1)/2; for(i = 1;i<=n;i++) for(j = 1;j<=n;j++) res -= dis[i][j]; printf("%d\n",res); return 0; }
1975题意:n个珠子(保证n是奇数),给出m对比较。a,b表示a比b重。问最终能确定多少个珠子必然不是中位数重量。
思路:和3275如出一辙,求传递闭包。
#include <cstdio> #include <queue> #include <algorithm> #include <cstring> using namespace std; #define INF 0x3fffffff #define clr(s,t) memset(s,t,sizeof(s)); #define N 105 int s[N][N],t[N][N],dis[N][N]; int T,n,m; int main(){ scanf("%d",&T); while(T--){ int i,j,k,a,b,flag = 0; scanf("%d %d",&n,&m); clr(dis, 0); for(i = 1;i<=n;i++) s[i][0] = t[i][0] = 0; for(i = 1;i<=m;i++){ scanf("%d %d",&a,&b); dis[a][b] = 1; s[a][++s[a][0]] = b; t[b][++t[b][0]] = a; } for(k = 1;k<=n;k++){ for(i = 1;i<=t[k][0];i++){ a = t[k][i]; for(j = 1;j<=s[k][0];j++){ b = s[k][j]; if(dis[a][k]+dis[k][b]==2 && !dis[a][b]){ dis[a][b] = 1; t[b][++t[b][0]] = a; s[a][++s[a][0]] = b; } } } } k = 0; for(i = 1;i<=n;i++){ a = b = 0; for(j = 1;j<=n;j++){ a += dis[i][j]; b += dis[j][i]; } if(a==n/2 && b==n/2){ flag = 1; break; } if(a>n/2 || b>n/2) k++; } if(flag) printf("%d\n",n-1); else printf("%d\n",k); } return 0; }