• 最小乘积生成树和最小乘积最大匹配


    两个知识的本质是一样的。都是每条边有k个权值(一般k为2),现在要取一个边集M使得其将所有点连通,并使每一种边权的总和的乘积最小。不同的是一个是生成树一个是匹配。

    对于这一类问题,我们都可以把每种方案的x之和与y之和作为它的坐标(x,y)

    要让乘积最小,那么可能的方案的坐标一定在一个下凸壳上。

    首先我们求出x最小的方案的坐标,再求出y最小方案的坐标

    这就是凸壳的两个端点A,B。

    然后考虑分治,每次找出离直线AB最远的点C,再继续处理

    要使距离最远,就是使向量AB和向量AC的叉积最大

    即最大化(c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x)

    即c.x*(b.y-a.y)+c.y*(a.x-b.x)       -a.x*(b.y-a.y)+a.y*(b.x-a.x)

    后面的一部分是常数,不用管。

    就是要使c.x*(b.y-a.y)+c.y*(a.x-b.x) 最大化

    对于生成树来说就用kruscal算法,匹配则是KM算法确定c,然后对AC、CB递归做同样的过程,直到找不到一个在左下的点C为止。

    最小乘积生成树算法:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <vector>
    using namespace std;
    #define N 205
    #define M 10100
    #define inf 0x3f3f3f3f
    struct Edge
    {
        int u,v;
        int a,b;
        int c;
        void read(){
            scanf("%d%d%d%d",&u,&v,&a,&b);
            u++;
            v++;
        }
    }e[M];
    bool cmpa(const Edge &a,const Edge &b){
        return a.a<b.a;
    }
    bool cmpb(const Edge &a,const Edge &b){
        return a.b<b.b;
    }
    bool cmpc(const Edge &a,const Edge &b){
        return a.c<b.c;
    }
    struct Point{
        int x,y;
        void print(){
            printf("%d %d
    ",x,y);
        }
        Point(int _x=0,int _y=0):x(_x),y(_y){}
        bool operator < (const Point &A) const
        {
            unsigned int p=x;
            p*=y;
            unsigned int q=A.x;
            q*=A.y;
            return p==q?x<A.x:p<q;
        }
    }ans,now,mina,minb;
    int f[N],n,m;
    int find(int x){
        return f[x]==x?f[x]:f[x]=find(f[x]);
    }
    Point kruscal()
    {
        int i,fa,fb;
        now=Point(0,0);
        for(int i=1;i<=n;i++)
            f[i]=i;
        for(int i=1;i<=m;i++){
            fa=find(e[i].u);
            fb=find(e[i].v);
            if(fa!=fb){
                f[fb]=fa;
                now.x+=e[i].a;
                now.y+=e[i].b;
            }
        }
        if(now<ans)
            ans=now;
        return now;
    }
    int xmul(const Point &A,const Point &B,const Point &C)
    {return (C.y-A.y)*(B.x-A.x)-(C.x-A.x)*(B.y-A.y);}
    void work(const Point &a,const Point &b)
    {
        for(int i=1;i<=m;i++){
            e[i].c=e[i].b*(a.x-b.x)+e[i].a*(b.y-a.y);
        }
        sort(e+1,e+m+1,cmpc);
        Point c=kruscal();
        if(xmul(a,b,c)<=0)
            return;
        work(a,c);
        work(b,c);
    }
    int main()
    {
    //  freopen("test.in","r",stdin);
    
        int i,j,k;
        int a,b,c;
        ans=Point(inf,inf);
    
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++)e[i].read();
        sort(e+1,e+m+1,cmpa),mina=kruscal();
        sort(e+1,e+m+1,cmpb),minb=kruscal();
        work(minb,mina),ans.print();
        return 0;
    }
    

    最小乘积匹配:

    #include<iostream>
    
    #include<cstring>
    #include<cstdio>
    #define inf 0x7fffffff
    struct poi{int x,y;}le,ri;
    int lx[75],ly[75],sla[75];
    int g[75][75],a[75][75],b[75][75],f[75];
    int n;
    bool vx[75],vy[75];
    bool operator ==(poi a,poi b){return a.x==b.x&&a.y==b.y;}
    using namespace std;
    bool dfs(int x)
    {
        vx[x]=true;
        for (int y=1; y<=n; y++)
        {
            if (!vy[y])
            {
                int t=lx[x]+ly[y]-g[x][y];
                if (!t)
                {
                    vy[y]=true;
                    if (!f[y]||dfs(f[y]))
                    {
                        f[y]=x;
                        return true;
                    }
                }
                else sla[y]=min(sla[y],t);
            }
        }
        return false;
    }
    poi km()
    {
        memset(lx,0,sizeof(lx)); memset(ly,0,sizeof(ly)); memset(f,0,sizeof(f));
        for (int i=1; i<=n; i++) for (int j=1; j<=n; j++)  lx[i]=max(lx[i],g[i][j]);
        for (int x=1; x<=n; x++)
        {
            memset(sla,63,sizeof(sla));
            while (true)
            {
                memset(vx,0,sizeof(vx)); memset(vy,0,sizeof(vy));
                if (dfs(x)) break;
                int d=inf;
                for (int i=1; i<=n; i++) if (!vy[i]) d=min(d,sla[i]);
                for (int i=1; i<=n; i++)
                {
                    if (vx[i]) lx[i]-=d;
                    if (vy[i]) ly[i]+=d;
                }
            }
        }
        poi ans=(poi) {0,0};
        for (int i=1; i<=n; i++) ans.x+=a[f[i]][i], ans.y+=b[f[i]][i];
        return ans;
    }
    int slove(poi l,poi r)
    {
        for(int i=1; i<=n; i++) for (int j=1; j<=n; j++) g[i][j]=a[i][j]*(r.y-l.y)+b[i][j]*(l.x-r.x);
        poi mid=km();
        if (l==mid||r==mid) return min(l.x*l.y,r.x*r.y);
        else
        return min(slove(l,mid),slove(mid,r));
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        for (int z=1; z<=t; z++)
        {
            scanf("%d",&n);
            for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) scanf("%d",&a[i][j]);
            for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) scanf("%d",&b[i][j]);
            for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) g[i][j]=-a[i][j];le=km();
            for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) g[i][j]=-b[i][j];ri=km();
            printf("%d
    ",slove(le,ri));
        }
    }
  • 相关阅读:
    实现Echarts折线图的虚实转换
    解决Webpack 安装sass时出现的错误
    Videojs视频插件在React中的应用
    overflow应用场景
    【JS Note】字符串截取
    【JS Note】undefined与null
    Web 网页常见问题集锦
    微信内置浏览器中,点击下拉框出现页面乱跳转现象(iphone)
    input美化 checkbox和radio样式
    Discuz!图片查看插件(支持鼠标缩放、实际大小、旋转、下载)
  • 原文地址:https://www.cnblogs.com/137033036-wjl/p/5916546.html
Copyright © 2020-2023  润新知