给你N个点,M条有向边,让你从中找到四个不重叠的点,使得其最短距离和最大。
输出这四个点的编号。
思路:
1、首先观察到点的个数和边的个数都不多,那么我们可以暴力的跑N次最短路,求得两点间距离,存入dis【i】【j】中,表示从i到j的最短路的长度。
2、那么接下来我们考虑如何寻找这四个点:
①如果我们暴力枚举的话,时间复杂度会高达O(n^4);明显是不可行的。
②那么我们此时可以考虑优化,我们枚举出三个点,作为第一个、第二个、和第三个点,那么其实贪心的去想,我们如果已经确定了这三个点的编号,那么最后一个点的编号一定是第三个点出发到的最远的那个和前两个点不重复的第一个点。而且维护一个点到其他点的最远距离时间复杂度O(n^2)就足够了,所以现在总时间复杂度降低了一个n:O(n^3);
③我们能够将第四个点通过上述过程优化找到的最主要原因是,其当前这个第四个点无论是哪个编号都不会影响前三个点的存在,如果我们现在枚举完前两个点,第三个点是第二个点的最远点,那么第四个点是第三个点的最远点,此时不一定就是最优解,因为第三个点到底是什么编号对第四个点产生了影响。所以我们显然不能直接这样枚举。我们继续考虑除了第四个点可以这样处理之外,还有哪个点可以这样处理?
④显然除了第四个点可以这样处理之外,我们可以选择第一个点。
那么此时我们O(n^2)枚举第二个和第三个点,第四个点是第三个点能够到达的最远点,那么同理,此时第二个点就是第一个点能够到达的最远点,那么我们O(n^2)预处理之后,再枚举即可。
⑤此时优化完毕,最终时间复杂度O(n^2);
3、注意这样一种情况,如果第二个点只是编号为zz的点能够到达的最远点,并且此时我们枚举出来的顺序为:第一个点zz,第二个点i,第三个点j,第四个点ss;
那么我们枚举到第二个点为i,第三个点为zz的时候,第二个点之前的那个第一个点就不是zz了,而是次远的点,所以这一块需要特殊处理一下即可:
我们将点i能够到达的点j距离进行从大到小排序存入vector,然后在枚举完第二个和第三个点的时候,我们第一个点可能不是zz,还有可能是次远点,所以这时候我们枚举一个最远点枚举一个次远点作为第一个点,同理,第四个点也枚举一个最远点,枚举一个次远点,维护最大即可。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
struct node2
{
int v,len;
}a[500085];
struct node
{
int from;
int to;
int w;
int next;
}e[50085];
int dis[3005][3005];
int vis[3005];
int head[3005];
int maxn[3005];
vector<int >mp[3005];
int maxn2[3005];
vector<int >mp2[3005];
int n,m,cont;
void add(int from,int to)
{
e[cont].to=to;
e[cont].w=1;
e[cont].next=head[from];
head[from]=cont++;
}
int cmp(node2 a,node2 b)
{
return a.len>b.len;
}
void SPFA(int ss)
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)dis[ss][i]=0x3f3f3f3f;
dis[ss][ss]=0;
vis[ss]=1;
queue<int >s;
s.push(ss);
while(!s.empty())
{
int u=s.front();
s.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
int w=e[i].w;
if(dis[ss][v]>dis[ss][u]+w)
{
dis[ss][v]=dis[ss][u]+w;
if(vis[v]==0)
{
vis[v]=1;
s.push(v);
}
}
}
}
for(int i=1;i<=n;i++)
{
a[i].len=dis[ss][i];
a[i].v=i;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(dis[ss][a[i].v]==0x3f3f3f3f)continue;
mp[ss].push_back(a[i].v);
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
cont=0;
int test=1;
memset(maxn,0,sizeof(maxn));
memset(maxn2,0,sizeof(maxn2));
memset(head,-1,sizeof(head));
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(i==0&&x==575&&y==2010)test=0;
add(x,y);
}
for(int i=1;i<=n;i++)
{
SPFA(i);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
a[j].len=dis[j][i];
a[j].v=j;
}
sort(a+1,a+n+1,cmp);
for(int j=1;j<=n;j++)
{
if(dis[a[j].v][i]==0x3f3f3f3f)continue;
mp2[i].push_back(a[j].v);
}
}
int mxlen=0;
int ans1,ans2,ans3,ans4;
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
if(dis[j][k]==0x3f3f3f3f)continue;
int flag=0;
for(int zz=0;zz<mp2[j].size()&&zz<3;zz++)
{
int i=mp2[j][zz];
for(int ss=0;ss<mp[k].size()&&ss<3;ss++)
{
int l=mp[k][ss];
if(i==j||i==k||i==l||j==k||j==l||k==l)continue;
else
{
flag=1;
if(mxlen<dis[i][j]+dis[j][k]+dis[k][l])
{
mxlen=dis[i][j]+dis[j][k]+dis[k][l];
ans1=i,ans2=j,ans3=k,ans4=l;
break;
}
}
}
if(flag==1)break;
}
}
}
//printf("%d\n",mxlen);
printf("%d %d %d %d\n",ans1,ans2,ans3,ans4);
}
}