测试地址:ZAW
题目大意: 给定一张边是双向的图,一条边走不同的方向可能代价不同,代价都非负,求从点
1
1
1出发,不经过重复的点或边的最小回路。
n
≤
5000
,
m
≤
10000
n\le 5000,m\le 10000
n≤5000,m≤10000。
做法: 本题需要用到最短路+二进制分组。
不久前做过一道清华爷出的NOIP模拟题:给定一张边是双向的图,给定其中的一个点集
C
C
C,求从
C
C
C中某点出发,到与这点不同的
C
C
C中的点结束的最短路。这道题不能直接建图的原因是,有起点和终点不能为同一个点的限制。为了取消这个限制,我们使用二进制分组的办法。
什么是二进制分组建图呢?我们发现,两个点不同,一定表示它们的编号的二进制表示中存在一位不同。这样一来,如果我们对于每个二进制位,把这一位为
0
0
0的设为起点集合,这一位为
1
1
1的设为终点集合,这样起点和终点集合没有交集,就直接采用建超级源点和超级汇点的方法即可。注意,因为同一条边不同方向的代价可能不同,所以还要把起点和终点集合颠倒再做一次。 这样一来,我们就用
log
n
\log n
logn次最短路,讨论完了所有可能的起点或终点的情况,最短路就用复杂度稳定的堆优化Dijkstra即可。
那么回到这一道题,因为边权非负,所以除了直接和点
1
1
1相连的边之外,其他的边重复走肯定是不会更优的,那么我们只需决定在点
1
1
1连接的点中,一开始从哪个点出发,最后到哪个点结束即可。我们发现这就和上面所说的那题一模一样了,使用上面所说的方法解决即可,时间复杂度为
O
(
m
log
m
log
n
)
O(m\log m\log n)
O(mlogmlogn)。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int n,m,S,T,a[10010],b[10010],u[10010],v[10010];
int first[5010],tot,ans,dis[5010];
bool vis[5010];
struct edge
{
int v,w,next;
}e[20010];
struct point
{
int id,val;
bool operator < (point a) const
{
return val>a.val;
}
}nxt;
priority_queue<point> Q;
void insert(int a,int b,int w)
{
e[++tot].v=b;
e[tot].w=w;
e[tot].next=first[a];
first[a]=tot;
}
void Dijkstra()
{
memset(vis,0,sizeof(vis));
dis[S]=0,vis[S]=1;
dis[T]=inf;
for(int i=first[S];i;i=e[i].next)
{
nxt.id=e[i].v;
nxt.val=e[i].w;
Q.push(nxt);
}
while(!Q.empty())
{
point now=Q.top();Q.pop();
while(vis[now.id]&&!Q.empty())
now=Q.top(),Q.pop();
if (vis[now.id]) break;
dis[now.id]=now.val;
vis[now.id]=1;
for(int i=first[now.id];i;i=e[i].next)
if (!vis[e[i].v])
{
nxt.id=e[i].v;
nxt.val=now.val+e[i].w;
Q.push(nxt);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&a[i],&b[i],&u[i],&v[i]);
if (b[i]==1) swap(a[i],b[i]),swap(u[i],v[i]);
}
S=n+1,T=n+2,ans=inf;
for(int i=0;(1<<i)<=n;i++)
{
for(int type=0;type<=1;type++)
{
memset(first,0,sizeof(first));
tot=0;
for(int j=1;j<=m;j++)
{
if (a[j]==1)
{
bool f=(b[j]&(1<<i));
f^=type;
if (f) insert(S,b[j],u[j]);
else insert(b[j],T,v[j]);
}
else insert(a[j],b[j],u[j]),insert(b[j],a[j],v[j]);
}
Dijkstra();
ans=min(ans,dis[T]);
}
}
printf("%d",ans);
return 0;
}