首页 > 其他 > 详细

最小乘积模型

时间:2020-03-03 19:52:45      阅读:74      评论:0      收藏:0      [点我收藏+]

最小乘积模型

每种物品有两个权值\(a_i,b_i\),求选出\(k\)个物品,使得\(\sum{a_i}*\sum{b_i}\)最小

考虑先求出\(\sum{a_i}\)\(\sum{b_i}\)最小的两种方案,记为\(A,B\)

我们将\(\sum{a_i}\)\(\sum{b_i}\)看作两个坐标,则\(A,B\)可以看作二维平面上的两个点

技术分享图片

据说图片来自\(hyj\)\(blog\),侵删

我们发现我们要找的点应该是位于\(A,B\)的左下方,且\(S_{ABC}\)最大的点

根据\(S_{ABC}=-\frac{AB×AC}{2}\)(此处是叉积)

可以得出我们要让\(AB\)\(AC\)的叉积最小(因为是负数)

化简一下

\[AB×AC=(x_B??x_A?)(y_C??y_A?)?(x_C??x_A?)(y_B??y_A?)=(x_B-x_A)*y_C+(y_A-y_B)*x_C-(x_B-x_A)*y_A+(y_B-y_A)*x_A\]

发现后两项中没有\(C\),我们将物品权值改为\(c_i=(x_B-x_A)*b_i+(y_A-y_B)*a_i\)

然后求最小\(\sum{c_i}\),递归到\(AB×AC\ge 0\)为止

例题

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
    inline int read()
    {
        int x=0;char ch,f=1;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} 
        return f?x:-x;
    }
    const int N=1e4+10;
    int n,m,suma,sumb;
    struct point
    {
        int x,y,a,b,c;
        inline bool operator < (const point &t) const{ return c<t.c; }
    }a[N];
    struct node
    {
        int x,y;
        inline node operator - (const node &t) const
        {
            return (node){x-t.x,y-t.y};
        }
        inline int cross(const node &t) const
        {
            return x*t.y-y*t.x;
        }
    }A,B,ret;
    int f[N];
    inline int find(int k){return f[k]==k?k:f[k]=find(f[k]);}
    inline node kruskal()
    {
        for(int i=1;i<=n;++i) f[i]=i;
        sort(a+1,a+m+1);
        int suma=0,sumb=0,tot=0;
        for(int i=1;i<=m;++i)
        {
            int x=find(a[i].x),y=find(a[i].y);
            if(x==y) continue;
            f[x]=y;
            suma+=a[i].a,sumb+=a[i].b;
            if(++tot==n-1) break;
        }
        if(1ll*suma*sumb<1ll*ret.x*ret.y||(1ll*suma*sumb==1ll*ret.x*ret.y&&suma<ret.x)) ret.x=suma,ret.y=sumb;
        return (node){suma,sumb};
    }
    inline void solve(node A,node B)
    {
        for(int i=1;i<=m;++i)
            a[i].c=(B.x-A.x)*a[i].b+(A.y-B.y)*a[i].a;
        node C=kruskal();
        if((B-A).cross(C-A)>=0) return;
        solve(A,C),solve(C,B);
    }
    inline void main()
    {
        n=read(),m=read();
        for(int i=1;i<=m;++i)
            a[i].x=read()+1,a[i].y=read()+1,a[i].a=a[i].c=read(),a[i].b=read();
        ret.x=ret.y=1e9+7;
        A=kruskal();
        for(int i=1;i<=m;++i) a[i].c=a[i].b;
        B=kruskal();
        solve(A,B);
        printf("%d %d",ret.x,ret.y);
    }
}
signed main()
{
    red::main();
    return 0;
}

最小乘积模型

原文:https://www.cnblogs.com/knife-rose/p/12404044.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!