洛谷P2738 [USACO4.1]篱笆回路Fence Loops 题解

洛谷P2738 [USACO4.1]篱笆回路Fence Loops 题解

题目链接:P2738 [USACO4.1]篱笆回路Fence Loops

题意:农夫布朗的牧场上的篱笆已经失去控制了。它们分成了1~200英尺长的线段。只有在线段的端点处才能连接两个线段,有时给定的一个端点上会有两个以上的篱笆。结果篱笆形成了一张网分割了布朗的牧场。布朗想将牧场恢复原样,出于这个考虑,他首先得知道牧场上哪一块区域的周长最小。 布朗将他的每段篱笆从1到N进行了标号(N=线段的总数)。他知道每段篱笆有如下属性:

该段篱笆的长度

该段篱笆的一端所连接的另一段篱笆的标号

该段篱笆的另一端所连接的另一段篱笆的标号

幸运的是,没有篱笆连接它自身。对于一组有关篱笆如何分割牧场的数据,写一个程序来计算出所有分割出的区域中最小的周长。

例如,标号1~10的篱笆由下图的形式组成(下面的数字是篱笆的标号):

           1
   +---------------+
   |\             /|
  2| \7          / |
   |  \         /  |
   +---+       /   |6
   | 8  \     /10  |
  3|     \9  /     |
   |      \ /      |
   +-------+-------+
       4       5

上图中周长最小的区域是由2,7,8号篱笆形成的。

这题建图比较麻烦

考虑将第 i i i 条边的两个端点记为 2 i − 1 2i-1 2i1 2 i 2i 2i ,记录其连接的边的编号

然后 O ( n 2 ) O(n^2) O(n2) 枚举一下这些边,将本质相同端点合并(用冰茶姬并查集)

去个重然后跑个最小环就好了

时间复杂度 O ( n 3 ) O(n^3) O(n3)

注意 INF不可以开太大,不然会爆掉的

边去重好像不必要,但是保险起见就去一去

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x1f1f1f1f1f1f1f1f
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N (int)(205)
namespace MERGE
{
    int fa[N];
    void init(int n){for(int i=1; i<=n; i++)fa[i]=i;}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void merge(int u,int v){fa[find(u)]=find(v);}
}using namespace MERGE;
int n,cnt;
struct InEdge
{
    int u,v,w,lto[N],rto[N];
}in[N];
int id[N],f[N][N],g[N][N];
signed main()
{
    read(n);init(2*n);
    for(int i=1,at,tl,tr; i<=n; i++)
    {
        read(at);
        in[at].u=2*at-1;in[at].v=2*at;
        read(in[at].w);read(tl);read(tr);
        for(int j=1,x; j<=tl; j++)
            read(x),in[at].lto[x]=1;
        for(int j=1,x; j<=tr; j++)
            read(x),in[at].rto[x]=1;
    }
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
        {
            if(i==j)continue;
            if(in[i].lto[j]&&in[j].lto[i])
                merge(in[i].u,in[j].u);
            if(in[i].lto[j]&&in[j].rto[i])
                merge(in[i].u,in[j].v);
            if(in[i].rto[j]&&in[j].lto[i])
                merge(in[i].v,in[j].u);
            if(in[i].rto[j]&&in[j].rto[i])
                merge(in[i].v,in[j].v);
        }
    for(int i=1; i<=2*n; i++)
        if(fa[i]==i)id[i]=++cnt;
    memset(g,0x1f,sizeof(g));
    memset(f,0x1f,sizeof(f));
    for(int i=1; i<=n; i++)
    {
        int &u=in[i].u,&v=in[i].v;
        u=id[find(u)];v=id[find(v)];
        g[u][v]=g[v][u]=min(g[u][v],in[i].w);
        f[u][v]=f[v][u]=min(f[u][v],in[i].w);
    }
    int ans=INF;n=cnt;
    // cout << cnt << endl;
    for(int k=1; k<=n; k++)
    {
        for(int i=1; i<k; i++)
            for(int j=i+1; j<k; j++)
                ans=min(ans,f[i][j]+g[i][k]+g[k][j]);
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
    }
    printf("%lld\n",ans);
    return 0;
}

转载请说明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值