zoukankan      html  css  js  c++  java
  • 最小乘积生成树和最小乘积最大匹配

    两个知识的本质是一样的。都是每条边有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));
        }
    }
  • 相关阅读:
    【基础算法】- 全排列
    【基础算法】- 2分查找
    区块链培训
    Static Binding (Early Binding) vs Dynamic Binding (Late Binding)
    test
    No data is deployed on the contract address!
    "throw" is deprecated in favour of "revert()", "require()" and "assert()".
    Variable is declared as a storage pointer. Use an explicit "storage" keyword to silence this warning.
    京都行
    Failed to write genesis block: database already contains an incompatible
  • 原文地址:https://www.cnblogs.com/137033036-wjl/p/5916546.html
Copyright © 2011-2022 走看看