题意:
给出一个二维平面上的 (m) 条边和边的端点的坐标,求出图的最小环的长度。
数据范围:
(1≤T≤50)
(1≤m≤4000)
(−10000≤x_i,y_i≤10000)
(1≤w≤10^5)
解法1:(暴力+ (dijsktra)剪枝)
删边,跑 (m) 次 (dijsktra) ,同时注意剪枝,对于当前队列中最小的长度,如果已经比答案要大,肯定不行。
一开始觉得这样做肯定超时,但看到网上的题解很多都是这做的,于是试了试。外加快读,链式前向星来优化,跑了 (343ms),有点惊讶。
代码:
#include <bits/stdc++.h>
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int M=4e3+5;
map<int,map<int,int> >mp;
struct node
{
int from,to,val,next;
}edge[M<<2];
int dis[M<<1],head[M<<1];
priority_queue<P,vector<P>,greater<P> >que;
int ans,cot;
void init()
{
memset(head,-1,sizeof(head));
cot=1;
}
void addedge(int from,int to,int w)
{
edge[cot].from=from;
edge[cot].to=to;
edge[cot].val=w;
edge[cot].next=head[from];
head[from]=cot++;
}
void read(int &x)
{
x=0;
int f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
x*=f;
}
int dij(int s,int t,int w)
{
while(!que.empty())
que.pop();
que.push(make_pair(0,s));
dis[s]=0;
while(!que.empty())
{
P now=que.top();
que.pop();
if(now.first+w>ans)
return inf;
if(dis[now.second]<now.first)
continue;
if(now.second==t)//不加会t,加了后:343ms
return dis[t];
for(int i=head[now.second];i!=-1;i=edge[i].next)
{
node tmp=edge[i];
if(tmp.val>=inf)
continue;
if(dis[tmp.to]>now.first+tmp.val)
{
dis[tmp.to]=now.first+tmp.val;
que.push(make_pair(dis[tmp.to],tmp.to));
}
}
}
return dis[t];
}
int main()
{
int t,cnt=0,m;
read(t);
while(t--)
{
init();
read(m);
mp.clear();
int a,b,c,d,e;
int num=0;
ans=inf;
for(int i=1;i<=m;i++)
{
read(a),read(b),read(c),read(d),read(e);
if(mp[a][b]==0)
mp[a][b]=++num;
if(mp[c][d]==0)
mp[c][d]=++num;
addedge(mp[a][b],mp[c][d],e);
addedge(mp[c][d],mp[a][b],e);
}
for(int i=1;i<cot;i+=2)
{
int tv=edge[i].val;
edge[i].val=inf;
edge[i+1].val=inf;
fill(dis+1,dis+num+1,inf);
int res=dij(edge[i].from,edge[i].to,tv);
ans=min(ans,res+tv);
edge[i].val=tv;
edge[i+1].val=tv;
}
printf("Case #%d: ",++cnt);
if(ans==inf)
printf("0
");
else
printf("%d
",ans);
}
return 0;
}
解法2:(MST+LCA)
最短的环除去一条边后一定是在这个图的最小生成树上
可以通过枚举不在树上的边,求树上这条边的两点间距离,加上边的权值并取个最小值即可
注意:题目给的图可能有多个联通块
复杂度:(O(nlogn))
用了 (78ms),因为(kruscal)忘记排序,用(lca)求树上两点间距离,公共祖先忘 (*2),(wa) 了两次,ε=(´ο`*)))唉。
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef pair<int,int> P;
const int inf=0x3f3f3f3f;
const int N=8e3+5;
const int mak=16;
struct edge
{
int from,to,val;
bool operator < (const edge b)const
{
return val<b.val;
}
};
vector<edge>e,s;
vector<P>pic[N];
map<int,map<int,int> >mp;
int fa[N],par[N][mak],depth[N],wt[N],vis[N];
int Find(int x)
{
if(x!=fa[x])
return fa[x]=Find(fa[x]);
else
return x;
}
void kruscal(int n)
{
int cnt=0,i;
s.clear();
for(int i=1;i<=n;i++)
pic[i].clear();
sort(e.begin(),e.end());//注意排序
for(i=0;i<e.size();i++)
{
int a=e[i].from;
int b=e[i].to;
int ta=Find(a);
int tb=Find(b);
if(ta!=tb)
{
fa[ta]=tb;
pic[a].pb(make_pair(e[i].val,b));
pic[b].pb(make_pair(e[i].val,a));
cnt++;
}
else
s.pb(e[i]);
if(cnt==n-1)
break;
}
for(i=i+1;i<e.size();i++)
s.pb(e[i]);
}
void dfs(int v,int p,int w,int d,int cnt)
{
par[v][0]=p;
depth[v]=d;
wt[v]=w;
vis[v]=cnt;
for(int i=0;i<pic[v].size();i++)
{
P u=pic[v][i];
if(u.second!=p)
dfs(u.second,v,w+u.first,d+1,cnt);
}
}
void init(int n,int &cnt)
{
fill(vis+1,vis+1+n,-1);
cnt=0;//树的个数
for(int i=1;i<=n;i++)
{
if(vis[i]==-1)
dfs(i,-1,0,0,++cnt);
}
for(int k=0;k+1<mak;k++)
{
for(int i=1;i<=n;i++)
{
if(par[i][k]==-1)
par[i][k+1]=-1;
else
par[i][k+1]=par[par[i][k]][k];
}
}
}
int lca(int u,int v)
{
if(vis[u]!=vis[v])//不在同一棵树
return -1;
if(depth[u]>depth[v])
swap(u,v);
for(int k=0;k<mak;k++)
{
if(((depth[v]-depth[u])>>k)&1)
v=par[v][k];
}
if(u==v)
return u;
for(int k=mak-1;k>=0;k--)
{
if(par[u][k]!=par[v][k])
{
u=par[u][k];
v=par[v][k];
}
}
return par[u][0];
}
int solve(int cnt)
{
int res=inf;
for(int i=0;i<s.size();i++)
{
int p=lca(s[i].from,s[i].to);
if(p==-1)
continue;
res=min(res,s[i].val+wt[s[i].from]+wt[s[i].to]-2*wt[p]);//注意*2
}
return res;
}
int main()
{
int t,m,cot=0;
scanf("%d",&t);
while(t--)
{
scanf("%d",&m);
mp.clear();
e.clear();
int num=0;
int a,b,c,d,f;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&f);
if(mp[a][b]==0)
mp[a][b]=++num;
if(mp[c][d]==0)
mp[c][d]=++num;
e.pb(edge{mp[a][b],mp[c][d],f});
}
int cnt=0;
for(int i=1;i<=num;i++)
fa[i]=i;
kruscal(num);
init(num,cnt);//cout<<"cnt="<<cnt<<endl;
int ans=solve(cnt);
if(ans==inf)
ans=0;
printf("Case #%d: %d
",++cot,ans);
}
return 0;
}