[51nod 1614]刷题计划

来源:互联网 发布:淘宝店铺招牌全屏代码 编辑:程序博客网 时间:2024/06/04 08:03

题目描述

大赛将至,摆在你面前的是n道题目,第 i(1 ≤ i ≤ n) 道题目能提升 ai 点智力值,代码量为 bi KB,无聊值为 ci ,求至少提升m点智力值的情况下,所做题目代码量之和*无聊值之和最小为多少。

最小乘积生成树

把每种方案的代码量和当做横坐标,无聊值和当做纵坐标,每种方案都可以用一个二维平面的点表示。
首先同在一个反比例函数上的点横纵乘积相同,我们要找一个点横纵乘积最小。
首先用01背包求出符合条件(条件指要满足智力值的下限)横坐标最小的点和纵坐标最小的点。
记为(x1,y1)和(x2,y2)
找到离这两点连线段距离最远的在其左下方的点。
设dx=|x1-x2|,dy=|y1-y2|,斜率k=dy/dx
那么对于(x,y),我们用过该点的斜率为k的直线与y轴的交点大小来刻画其打连线段距离的远近。
那么交点大小为kx+y
现在要找到这个估量值最小的点
返回来看第i道题目,对答案贡献为dy/dxb[i]+c[i]
为了方便,转化为整数dyb[i]+c[i]dx
设d[i]表示这个估量值,然后做01背包,于是我们又找到一个点C
这里写图片描述
显然三角形内的点都不会比C优,因为其所处反比例函数一定不比C所在反比例函数更靠近原点。
接下来递归AC和BC继续计算。
复杂度不会证……

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=1600+10;ll f[maxn][maxn],g[maxn][maxn];ll a[maxn],b[maxn],c[maxn],d[maxn];ll i,j,k,l,t,n,m,ans,x,y,x1,y1,x2,y2,dx,dy;void dp(){    int i,j,k;    fo(i,0,n)        fo(j,0,800)            f[i][j]=1000000000000;    f[0][0]=0;    fo(i,0,n-1)        fo(j,0,800)            if (f[i][j]!=1000000000){                if (f[i][j]+d[i+1]<f[i+1][j+a[i+1]]){                    f[i+1][j+a[i+1]]=f[i][j]+d[i+1];                    g[i+1][j+a[i+1]]=1;                }                if (f[i][j]<f[i+1][j]){                    f[i+1][j]=f[i][j];                    g[i+1][j]=0;                }            }    k=-1;    fo(i,m,800)        if (f[n][i]!=1000000000&&(k==-1||f[n][i]<f[n][k])) k=i;    x=y=0;    fd(i,n,1){        if (g[i][k]){            k-=a[i];            x+=b[i];            y+=c[i];        }    }}void solve(ll x1,ll y1,ll x2,ll y2){    dx=abs(x1-x2);    dy=abs(y1-y2);    ll i;    fo(i,1,n) d[i]=b[i]*dy+c[i]*dx;    dp();    ll xx=x,yy=y;    if ((xx==x1&&yy==y1)||(xx==x2&&yy==y2)) return;    else{        ans=min(ans,xx*yy);        solve(x1,y1,xx,yy);        solve(xx,yy,x2,y2);    }}int main(){    scanf("%lld%lld",&n,&m);    fo(i,1,n) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);    fo(i,1,n) d[i]=b[i];    dp();    x1=x;y1=y;    fo(i,1,n) d[i]=c[i];    dp();    x2=x;y2=y;    ans=min(x1*y1,x2*y2);    solve(x1,y1,x2,y2);    printf("%lld\n",ans);}
0 0
原创粉丝点击