Pandaland HDU - 6005【最小环】

题意:

给出一个二维平面上的 \(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\n");
        else
            printf("%d\n",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\n",++cot,ans);
    }
    return 0;
}

posted @ 2020-03-17 22:39  xzx9  阅读(165)  评论(0编辑  收藏  举报