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;
}