Codeforces Round #349 (Div. 2) D. World Tour

题意:

给出n个点m条边的有向图, 每条边的长度为1

要求找出四个点a,b,c,d使得 a->b + b->c + c->d 的距离最大 其中a->b ,b->c ,c->d代表两点之间的最短路径

(1<=n<=3000) (1<=m<=5000)

思路

因为时间给了 5 秒,所以枚举其中的两个点肯定没问题,但是再多枚举一个点都是不可能的了。那么我们就枚举两个点。枚举哪两个点呢?先考虑枚举起点和终点。不过即使枚举出起点和终点,我们也很难求出中间的点,因为变化实在太多(如果只走三个点的话这样枚举或许是可行的)。然后再考虑枚举中间两个点。假设枚举到的中间两个点为 i,j ,那么如果能求出到i点距离最远的点和j点能到达的最远的点(根据题意两点之间“距离”表示的是两点之间的最短路径长度),这样枚举就是可行的。我们可以通过 BFS (不需要用 Dijkstra SPFA ,因为每条边的长度都是相同的)预处理求出任意两点之间的最短路径长度。然后对于每个点得到两个序列 d1,d2 d1[i] 表示所有能够到达 i 的不是i的点按照到i的距离从大到小排序得到的序列, d2[i] 表示所有从 i 出发能够到达的不是i的点按照i到它的距离从大到小排序得到的序列。那么,为什么要求出这么个序列而不是直接求距离最大的点呢?因为题目要求的 4 个点不能重复。也就是说,如果我们求出到中间两点距离最大的两个点 s,t ,那么 s,t 可能会和 i,j 发生重复。因此我们应该得到 d1,d2 序列。对于我们枚举出的点 i,j ,再枚举 d1[i] d2[j] 中的点 ii jj 3 个。为什么只枚举 3 个点呢?因为 i,j 是两个点。只有枚举 3 个点才能保证至少有一个点不与 i,j 重复(其实我觉得为了防止 s,t 重复至少应该枚举 4 个点,但是 3 个点也 AC 了)。最后就可以根据 iiijjj 这条路径的长度更新最长路径了。

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF  0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 10010
#define MOD 1000000007

struct Edge
{
    int to,next;
}edge[maxn];
int n,m;
int tot,head[maxn],vis[maxn];
int dis[3030][3030];
vector<pair<int,int> > d1[maxn],d2[maxn];
void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
void add_edge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void bfs(int u)
{
    queue<int> q;
    q.push(u);
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        for(int i = head[x]; i != -1; i = edge[i].next)
        {
            int y = edge[i].to;
            if(dis[u][y] || y == u)
                continue;
            dis[u][y] = dis[u][x] + 1;
            q.push(y);
        }
    }
}

int main()
{
    int t,C = 1;
    //scanf("%d",&t);
    while(scanf("%d%d",&n,&m) != EOF)
    {
        init();
        for(int i = 0; i < m; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add_edge(x,y);
        }
        memset(dis,0,sizeof(dis));
        for(int i = 1; i <= n; i++)
            bfs(i);
        for(int i = 1; i <= n; i++)
        {
            d1[i].clear();
            d2[i].clear();
        }
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
        {
            if(!dis[i][j])
                continue;
            d2[i].push_back(make_pair(dis[i][j],j));
            d1[j].push_back(make_pair(dis[i][j],i));
        }
        for(int i = 1; i <= n; i++)
        {
            sort(d1[i].rbegin(),d1[i].rend());
            sort(d2[i].rbegin(),d2[i].rend());
        }
        int ans = 0;
        int v1,v2,v3,v4;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
        {
            if(!dis[i][j])
                continue;
            for(int ii = 0; ii < min(4,(int)d1[i].size()); ii++)
                for(int jj = 0; jj < min(4,(int)d2[j].size()); jj++)
            {
                int u = d1[i][ii].second;
                int v = d2[j][jj].second;
                if(u == j || i == v || !dis[u][v])
                    continue;
                if(dis[u][i] + dis[i][j] + dis[j][v] > ans)
                {
                    ans = dis[u][i] + dis[i][j] + dis[j][v];
                    v1 = u;
                    v2 = i;
                    v3 = j;
                    v4 = v;
                }
            }
        }
        //printf("%d\n",ans);
        printf("%d %d %d %d\n",v1,v2,v3,v4);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值