电缆建设 jzoj 1405 二分图匹配 网络流

题目大意

  教主上电视了,但是蔚蓝城郊区沿河的村庄却因电缆线路老化而在直播的时候停电,这让市长SP先生相当的愤怒,他决定重修所有电缆,并改日播放录像,杜绝此类情况再次发生。
  河流两旁各有n,m个村庄,每个村庄可以用二维坐标表示,其中河流一旁的村庄横坐标均为x1,河流另一旁的村庄横坐标均为x2。由于地势十分开阔,任意两个村庄可以沿坐标系直线修建一条电缆连接,长度即为两村庄的距离。要修建若干条电缆,使得任意两个村庄都可以通过若干个有电缆连接的村庄相连。
  因为修建的经费与长度成正比,SP市长当然希望所花的钱越少越好,所以他希望你来帮助他设计一套方案,使得电缆总长度最小,并告诉所需要的电缆总长度。

分析

本题即为平面图上的MST,特殊的是只有两排点。
普通的MST算法可以拿到40%的分数。但是由于图的特殊性,有以下推论:

1、MST中不会有线段相交,若AD与BC相交,无论AB有不经过AD、BC路径相连还是CD有不经过AD、BC路径,改连AC,BD会使得生成树权和更小。

2、若有点A(x1,y1),B(x1,y2),C(x2,y3),且y1 < y2 < y3,那么边AC不可能在MST里。由于线段不交叉,B、C上方的点不可能与下方的点有连接,所以在MST中,不考虑B、C以下点的情况下,B、C要么同时属于MST中的一棵树中(B C连通),要么分别属于两棵树(B C不连通)。在BC连通的情况下,连接BA与连接AC同样将树扩展到了A及以上,由于ABC为钝角三角形,AB < AC,所以连BA只会使结果更优。在BC不连通的情况下,单连AC会使树不连通,再连AB或BC等价于连AB与BC,但是权更大。
所以这种情况AC是不可能连边。
这样就可以构出一张边数最多为2(m+n)条的新图,使用Kruskal时间复杂度O((n+m)log(n+m))期望得分为70-100。

code

#include
#include
#include
#include
#include
#include

using namespace std;

struct arr{
    int x,y;
    double w;
    int next;
}edge[4000000];
int ls[2000000];
int n,m;
int edge_m;

bool cmp(arr a,arr b)
{
  return a.wint f[2000000];

int find(int x)
{
    if (f[x]==x) return x;
    else{
        f[x]=find(f[x]);
        return f[x];
    }
}

void merge(int x,int y)
{
    int x1,y1;
    x1=find(x);
    y1=find(y);
    f[x1]=y1;
}

void add(int x,int y,double w)
{
    edge_m++;
    edge[edge_m]=(arr){x,y,w};
}

int x[1000000];
int y[1000000];

double sqr(double x)
{
    return x*x;
}

int main()
{
    int x1,x2;
    scanf("%d%d%d%d",&n,&m,&x1,&x2);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
        x[i]+=x[i-1];
    }
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&y[i]);
        y[i]+=y[i-1];
    }
    for (int i=2;i<=n;i++)
        add(i-1,i,x[i]-x[i-1]);
    for (int i=2;i<=m;i++)
        add(i+n-1,i+n,y[i]-y[i-1]);
    int j=1;
    for (int i=1;i<=n;i++)
    {
        while ((y[j]<=x[i])&&(j<=m)) j++;
        if (j>1) add(i,j+n-1,sqrt(sqr(x1-x2)+sqr(y[j-1]-x[i])));
        if (j<=m)add(i,j+n,sqrt(sqr(x1-x2)+sqr(y[j]-x[i])));
    }
    sort(edge+1,edge+edge_m+1,cmp);
    for (int i=1;i<=n+m;i++) f[i]=i;
    double ans=0;
    int num=0;
    int i=1;
    while ((numm-1)&&(i<=edge_m))//Kruskal?????? 
    {
        if (find(edge[i].x)!=find(edge[i].y))
        {
            merge(edge[i].x,edge[i].y);
            ans+=edge[i].w;
            num++;
        }
        i++;
    }
    printf("%0.2f",ans);
}

你可能感兴趣的:(c++,网络流,最大匹配)