问了博哥得知这是一道背包非常震惊.然而还是不会做,看题解研究了两个小时.
题意
有很多杯子,每个杯子有容量 a a a以及当前装的水 b b b.每次可以将一个杯子里的水倒入另一个杯子,但是有 50 % 50\% 50%的损耗,并且杯子里的水不能超过其容量,否则也将损耗.询问将水全部存储至 k k k个杯子里面的时候能够存储的最大水量,输出 k k k从 1 1 1取到 n n n时的答案,每个询问相互独立.
题解
用
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示前
i
i
i个杯子中取
j
j
j个并且能够存储的总水量为
k
k
k时的最大答案.
假设我们选的杯子总容量为
A
A
A,总水量为
B
B
B,剩下的杯子还能倒出
(
S
−
B
)
2
\frac{(S-B)}{2}
2(S−B)的水,从而我们能够得到的答案即为
m
i
n
(
A
,
S
+
B
2
)
min(A,\frac{S+B}{2})
min(A,2S+B).
因此转移方程为
d
p
[
i
]
[
k
]
[
A
]
=
max
(
d
p
[
i
−
1
]
[
k
]
[
A
]
,
d
p
[
i
−
1
]
[
k
−
1
]
[
A
−
a
[
i
]
]
+
b
[
i
]
)
dp[i][k][A] = \max(dp[i - 1][k][A], dp[i - 1][k - 1][A - a[i]] + b[i])
dp[i][k][A]=max(dp[i−1][k][A],dp[i−1][k−1][A−a[i]]+b[i]).
取最大答案的时候
a
n
s
[
k
]
=
min
(
A
,
d
p
[
n
]
[
k
]
[
A
]
/
2
+
B
/
2
)
)
ans[k] = \min(A, dp[n][k][A] / 2 + B / 2))
ans[k]=min(A,dp[n][k][A]/2+B/2)).
代码如下.
d
p
dp
dp数组采用滚动数组可以减少一维.
const int _=105;
int dp[_][_*_],a[_],b[_];
int main() {
int i,n,s=0,j,k;
read(n);
memset(dp,0xcf,sizeof dp),**dp=0;
for (i=1;i<=n;++i) read(a[i]),read(b[i]);
for (i=1;i<=n;++i) {
for (s+=b[i],j=i;j;--j) {
for (k=10000;k>=a[i];--k)
dp[j][k]=max(dp[j][k],dp[j-1][k-a[i]]+b[i]);
}
}
for (i=1;i<=n;++i) {
int lxy=0;
for (j=0;j<=10000;++j)
lxy=max(lxy,min(j*2,dp[i][j]+s));
printf("%.9lf ",lxy/2.0);
}
}
谢谢大家.