题目大意:n个路由器,编号1-n,26个公司,编号a-z,路由器之间有一些有向边,边权为一个字符串,字符串由小写字母组成,表示字符串对应的公司能使这条边连通。现在给若干个查询,查询能使任意2个路由器连通的公司。
题目分析:题目就是要求能使任意2个路由器连通的公司的集合。公司只有26个,求集合一般用状态压缩。因为要查询任意2个路由器,所以要知道任意2个路由器连通的公司集合。n范围200,所以可以floyd。想到了floyd和状态压缩,那么代码也就比较好写了。
floyd的本质是枚举中间节点k,使节点i到j的距离最大或最小。针对本题,是要求一个集合,使从i到j连通的公司,那么枚举k的时候,就要求保证i->k和k->j同时连通的公司,状态压缩的话,直接将dis[i][k]和dis[k][j]相与便是结果,这个结果要加到dis[i][j]上去,所以再和dis[i][j]相或。所以总的方程就是:
dis[i][j] = dis[i][j] | (dis[i][k]&dis[k][j])。
详情请见代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 205;
int dis[N][N];
int n,m;
void Floyd()
{
int i,j,k;
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]);
}
int main()
{
int a,b,i;
char s[30];
while(scanf("%d",&n),n)
{
memset(dis,0,sizeof(dis));
while(scanf("%d%d",&a,&b),(a+b))
{
scanf("%s",s);
int len = strlen(s);
for(i = 0;i < len;i ++)
dis[a][b] |= (1<<(s[i] - 'a'));
}
Floyd();
while(scanf("%d%d",&a,&b),(a+b))
{
if(dis[a][b])
{
for(i = 0;i < 26;i ++)
if(dis[a][b]&(1<<i))
putchar('a' + i);
}
else
putchar('-');
putchar(10);
}
putchar(10);
}
return 0;
}
//792K 47MS