题目大意:
一个N个点,M条有向边的图,给出K个关键点,让我们从这K个关键点中,选择一个点作为起点,一个点作为终点,使得从起点到终点的最短距离是所有选择中最小的。问这个最短距离长度。
思路:
①我们在一个集合中找两个点显然有些难度,我们假设有一个起点集合,一个终点集合的话,我们建立一个超级源点连入起点集合的各个点,然后再建立一个超级汇点,将终点集合的各个点连入这个超级汇点的话,我们跑一遍从超级源点到超级汇点的最短路就很简单能够解决问题。
②但是我们现在没有这两个集合,我们不妨考虑将关键点集合分出两个集合,那么我们怎样分能够使得出错率最小呢?我们显然希望对半分。
所以我们随机这K个关键点的排列顺序,然后前半部分作为起点集合,后半部分作为终点集合去跑就行了。
③随机一次出来的结果出错率为3/4(正确的概率:起点分布正确的概率是1/2.终点分布正确的概率是1/2.相乘为1/4).两次都出错的概率为3/4*3/4.三次都出错的概率为3/4*3/4*3/4.依次类推,显然随机的次数越多,正确结果的概率越大。我们只需要其中任意一次正确即可。所以这样做的正确率是可以保证的。
所以我们随机20次左右就足够了,过程跑SPFA,维护最小解即可。
Ac代码:
#include
#include
#include
#include
using namespace std;
#define ll __int64
struct node
{
ll from,to,next,w;
} e[150000];
ll cont,n,m,K,ss,tt,ans;
ll xx[150000];
ll yy[150000];
ll ww[150000];
ll ned[150000];
ll head[150000];
ll dist[150000];
ll vis[150000];
void add(ll from,ll to,ll w)
{
e[cont].w=w;
e[cont].to=to;
e[cont].next=head[from];
head[from]=cont++;
}
void SPFA()
{
queues;
memset(vis,0,sizeof(vis));
for(ll i=1; i<=tt; i++)dist[i]=0x3f3f3f3f;
dist[ss]=0;
s.push(ss);
while(!s.empty())
{
ll u=s.front();
s.pop();
vis[u]=0;
for(ll i=head[u];i!=-1;i=e[i].next)
{
ll v=e[i].to;
ll w=e[i].w;
if(dist[v]>dist[u]+w)
{
dist[v]=dist[u]+w;
if(vis[v]==0)
{
vis[v]=1;
s.push(v);
}
}
}
}
ans=min(ans,dist[tt]);
}
void Slove()
{
ans=0x3f3f3f3f;
ll temp=20;
while(temp--)
{
ss=n+1;
tt=n+2;
cont=0;
memset(head,-1,sizeof(head));
random_shuffle(ned,ned+K);
for(ll i=1; i