1. 程式人生 > 實用技巧 >UVA 12589 Learning Vector(01揹包+貪心(微擾))

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\n", ++kase, ans);
    }
    return 0;
}