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\)。设\(x_jy_i - x_iy_j > 0\),显然,如果要第一个方案比二个更优就要把斜率大的放前面。
代码
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;
}