最短路进阶一:floyd传递闭包问题[POJ-3660]

会有这样的题目,数据量较小,给出两点间之间的关系,求最后是否矛盾(并查集:不过那道题还没有弄懂),或者求出一共有几层关系(火车排队:拓扑排序最长路),以及求出所有能够满足的关系
简单来说:这里我们要求求出所有的关系,通过已给出的关系。

题目:POJ-3660
要求判断给出的点是否能确立和其他的点的大小关系,通过已给出的大小关系。
可以floyd求解:
做法一:先读入关系,但必须是有向边,否则处理会有误。然后跑一遍Floyd,松弛操作具体为两条小路皆合法才合法。最后判断的时候,因为求的是 &gt; &gt; >关系,所以还判断一下是否存在 &lt; &lt; <的关系。
做法二:就是对于每个点,如果是可以知道所有关系的,指向自己的点和自己指向的点一定等于 n − 1 n-1 n1。这句话的意思很简单,就是 d f s dfs dfs子树下的所有结点,正反向建图即可。
这里我只写了做法一解法的代码。仅供参考。

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

#define ll long long

int n,m;
const int maxn = 404;

bool d[maxn][maxn];

void floyd(){
    for(int i=1;i<=n;i++)d[i][i]=1;
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    d[i][j]=max(d[i][j],d[i][k]&&d[k][j]);
}

int main(){
    cin>>n>>m;
    memset(d,0,sizeof(d));
    FOR(i,1,m){
        int x,y;scanf("%d%d",&x,&y);
        d[x][y]=1;
    }
    floyd();
    int ans=0;
    for(int i=1;i<=n;i++){
        int cnt=0;
        for(int j=1;j<=n;j++)if(d[i][j]||d[j][i])cnt++;
        if(cnt==n)ans++;
    }
    cout<<ans<<endl;
}

事实上 t a r j a n tarjan tarjan也可以处理下面这类问题,不过小数据可以用 f l o y d floyd floyd做。
U v a 247 Uva247 Uva247
这道题题意, A 给 B A给B AB打电话,给出多种这样的关系,如果这样能一直传递到某个 Z Z Z A A A,这就是个电话圈。
所以我们可以这样处理: d [ i ] [ j ] = d [ i ] [ j ] ∣ ∣ ( d [ i ] [ k ] d[i][j]=d[i][j]||(d[i][k] d[i][j]=d[i][j](d[i][k]&& d [ k ] [ j ] ) d[k][j]) d[k][j])
传递闭包。如果最后两者能够互相传递即是答案。(a能到达b,b也要可以回到a)所以即使闭包(有向环)

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define ull unsigned long long
using namespace std;

const int maxn = 100;

int n,m;
int d[maxn][maxn];
int flag[maxn];
map<string,int>M;
vector<int>G[maxn];
string A[maxn];

int main(){
    int cnt,a,b;
    int kase=0;
    while(cin>>n>>m){
        if(n==0&&m==0)break;
        cnt=0;
        M.clear();
        memset(d,0,sizeof(d));
        memset(flag,0,sizeof(flag));
        FOR(i,1,n)G[i].clear();
        string str;
        FOR(i,1,m){
            cin>>str;
            if(!M[str])a=M[str]=++cnt;
            else a=M[str];
            A[a]=str;
            cin>>str;
            if(!M[str])b=M[str]=++cnt;
            else b=M[str];
            A[b]=str;
            d[a][b]=1;
        }
        for(int i=1;i<=n;i++)d[i][i]=1;
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++){
                    d[i][j]=d[i][j]||(d[i][k]&&d[k][j]);
                }
        if(kase)puts("");
        printf("Calling circles for data set %d:\n",++kase);
        string ans="";
        for(int i=1;i<=n;i++)if(!flag[i]){
            flag[i]=1;
            ans="";
            for(int j=1;j<=n;j++)if(d[i][j]&&d[j][i]){
                ans+=A[j]+", ";
                flag[j]=1;
            }
            if(ans!=""){
                for(int j=0;j<ans.size()-2;j++)printf("%c",ans[j]);
            }
            puts("");
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值