UVA 12589 Learning Vector(01背包+贪心(微扰))

题目链接

题目大意

  给n个向量从中选出k个向量,使得它们首尾连接与x轴成的面积的2倍最大。

解题思路

  首先我们观察图形可以发现,每新加一个向量,图像右上角的横纵坐标都会变化,而且计算面积的时候需要知道前面的纵坐标,所以我们可以dp图像的纵坐标,其值就是面积,然后取最大面积。既然是从n个数中选出k个数,很明显就是一个01背包模型。
  但是还没完,还要考虑添加的顺序问题,这里我们用微扰(临项交换)来解。设前面所选方案所得的高度为h,然后又选了两个坐标,分别是((x_i,y_i),(x_j,y_j)),我们计算出这个方案的结果,然后再交换两个坐标的顺序再计算一遍结果,让两个结果做差,发现结果和斜率有关。
  交换前面积(S_1 = 2(hx_i)+x_iy_i+2(h+y_i)x_j+x_jy_j)
  交换后面积(S_2 = 2(hx_j)+x_jy_j+2(h+y_j)x_i+x_iy_i)
  (S_1-S_2 = x_jy_i - x_iy_j)。显然,如果要第一个方案比二个更优就要把斜率大的放前面。

代码

const int maxn = 2500+10;
const int maxm = 2e2+10;
struct INFO {
    int x, y;
} a[55];
ll dp[55][2505]; int n, m, kase;
int main() {
    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        int sum = 0;
        for (int i = 1; i<=n; ++i) {
            scanf("%d%d", &a[i].x, &a[i].y);
            sum += a[i].y;
        }
        clr(dp, -1); dp[0][0] = 0;
        sort(a+1, a+n+1, [](INFO a1, INFO a2) {return a2.x*a1.y>a1.x*a2.y;});
        ll ans = 0;
        for (int i = 1; i<=n; ++i)
            for (int j = m; j>=1; --j)
                for (int k = sum; k>=a[i].y; --k) 
                    if (dp[j-1][k-a[i].y]>=0) {
                        dp[j][k] = max(dp[j][k], dp[j-1][k-a[i].y]+2LL*a[i].x*(k-a[i].y)+1LL*a[i].x*a[i].y);
                        ans = max(ans, dp[j][k]);
                    }
        printf("Case %d: %lld
", ++kase, ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shuitiangong/p/13565804.html