poj2570&zoj1967Fiber Network(floyd+状态压缩)

题目请戳这里

题目大意: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


你可能感兴趣的:(图论,最短路)