zoukankan      html  css  js  c++  java
  • 最小乘积模型

    最小乘积模型

    每种物品有两个权值(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×ACge 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;
    }
    
  • 相关阅读:
    DotNet中的迭代模式和组合模式
    能以可视化方式编辑滤镜效果的Image控件
    自定义ExtenderControl实现服务器控件可拖放
    股票交易费及利润计算器
    用ajax library的客户端脚本实现无刷新分页
    实现html转Xml
    oracle10g主机身份证明错误的解决办法
    创建ASP.NET AJAX客户端组件实现验证控件的toolTip式错误提示
    可分页的Repeater控件
    数据结构趣题——约瑟夫环
  • 原文地址:https://www.cnblogs.com/knife-rose/p/12404044.html
Copyright © 2011-2022 走看看