背包问题系列——背包问题6
由2021/8/6艰难复习有感而作。
问题描述
一个背包容积为\(T(0 \leq T<=5000)\),现在有\(N(1 \leq N \leq 200)\)种物品,每种物品有一定体积\(V(1 \leq V \leq 5000)\)。每种物品有一定价值\(W(1\leq W \leq 5000)\)。每种物品有无限多个。从这\(N\)种物品中选取若干个装入背包内,使背包所有物品总价值最大。 上面求最大价值是朴素的背包问题。本题要求你计算出能得到的第\(K(1 \leq K\leq 50)\)大价值。两种方案只要总价值相同就认为是相同的。
输入格式
第一行,三个整数\(n, T, k\)
第二行,\(n\)个整数,表示每个物品的价值
第三行,\(n\)个整数,表示每个物品的体积
输出格式
一个整数,表示能得到的第k大价值
样例输入 1
5 10 2
1 2 3 4 5
5 4 3 2 1
样例输出 1
12
样例输入 2
5 10 12
1 2 3 4 5
5 4 3 2 1
样例输出 2
2
众所周知,完全背包是这样做的:
for(int i = 1; i <= n; i++)
for(int j = T; j >= v[i]; j--)
{
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
但是我们是要求前\(k\)大的价值。
所以我们设\(dp[j]\)为当前用了\(j\)的体积,然后我们可以新开一维,表示前\(k\)大的价值。
然后前\(k\)大的价值怎么算呢?我们来看一下完全背包是怎么算的:
然后因为我们要求前\(k\)大的价值,怎么求?我们可以这样做:
for(int t = 1; t <= k; t++)
{
tmp1[t] = dp[j][t];
tmp2[t] = dp[j - v[i]][t] + w[i];
}
然后我们就算出来了\(dp[j]\)的前\(k\)大价值和\(dp[j - v[i]]\)的前\(k\)大价值。
然后我们把tmp1和tmp2合并成一个数组tmp3,这里我写了一个函数,因为类似归并排序中的一步,所以叫getsort。
void getsort(int *a, int *b, int *c)//这里就是看tmp1[ka]大还是tmp2[kb]大,哪个大就先把哪个放到tmp3[t]里面去,保证取得是tmp1和tmp2中最大的前2*k个
{
for(int t = 1, ka = 1, kb = 1; t <= k + k; )
{
if(kb > k || (ka <= k && a[ka] >= b[kb]))
c[t++] = a[ka++];
else
c[t++] = b[kb++];
}
}
但是由于题目中说了两种方案只要总价值相同就认为是相同的。,所以我们需要去重。
int t = unique(tmp3 + 1, tmp3 + k + k + 1) - tmp3 - 1;
这样我们就把dp[j]的前\(k\)大价值算出来了,我们把他复制进去的同时更新一下答案:
for(int t = 1; t <= min(t, k); t++)
{
dp[j][t] = tmp3[t];
cnt++;
tmp4[cnt] = dp[j][t]; //这里就把答案存进来了
}
然后最后,答案肯定是有重复的,我们再去一次重。
sort(tmp4 + 1, tmp4 + cnt + 1); // 去重之前要排序
int anscnt = 0;
for(int i = 1; i <= cnt; i++)
if(tmp4[i] > 0 && tmp4[i] != tmp4[i - 1])
{
anscnt++;
ans[anscnt] = tmp4[i]; //把去重后的数组放到ans里
}
printf("%d\n", ans[anscnt - k + 1]); //因为是从小到大排序的,不像上面是从大到小排序的,所以就得输出`ans[anscnt - k + 1]`
交!
好耶!
AC*2
TLE*1(???TLE就离谱)
RE*9
俗话说得好,万紫千红总是春!
那我们来改改吧!
我把函数里的东西复制到main函数里来:
for(int t = 1, ka = 1, kb = 1; t <= k + k; )
{
if(kb > k || (ka <= k && tmp1[ka] >= tmp2[kb]))
tmp3[t++] = tmp1[ka++];
else
tmp3[t++] = tmp2[kb++];
}
然后我把以前的更新答案删掉,放到外面来:
for(int j = T; j >= 0; j--)
{
for(int t = 1; t <= k; t++)
{
cnt++;
tmp4[cnt] = dp[j][t];
}
}
然后把ans数组取了反:
reverse(ans + 1, ans + anscnt + 1);
再交
???A了?
#include <bits/stdc++.h>
using namespace std;
int n, T, k;
int cnt;
int v[205], w[205];
int dp[5005][55];
int tmp1[5005], tmp2[5005], tmp3[5005], tmp4[2500005];
int ans[2500005];
void getsort(int *a, int *b, int *c)
{
}
int main()
{
scanf("%d %d %d", &n, &T, &k);
for(int i = 1; i <= n; i++)
scanf("%d", &w[i]);
for(int i = 1; i <= n; i++)
scanf("%d", &v[i]);
memset(dp, -0x3f, sizeof(dp));
dp[0][1] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = T; j >= v[i]; j--)
{
for(int t = 1; t <= k; t++)
{
tmp1[t] = dp[j][t];
tmp2[t] = dp[j - v[i]][t] + w[i];
}
for(int t = 1, ka = 1, kb = 1; t <= k + k; )
{
if(kb > k || (ka <= k && tmp1[ka] >= tmp2[kb]))
tmp3[t++] = tmp1[ka++];
else
tmp3[t++] = tmp2[kb++];
}
int t = unique(tmp3 + 1, tmp3 + k + k + 1) - tmp3 - 1;
for(int x = 1; x <= min(t, k); x++)
dp[j][x] = tmp3[x];
}
}
for(int j = T; j >= 0; j--)
{
for(int t = 1; t <= k; t++)
{
cnt++;
tmp4[cnt] = dp[j][t];
}
}
sort(tmp4 + 1, tmp4 + cnt + 1);
int anscnt = 0;
for(int i = 1; i <= cnt; i++)
if(tmp4[i] > 0 && tmp4[i] != tmp4[i - 1])
{
anscnt++;
ans[anscnt] = tmp4[i];
}
reverse(ans + 1, ans + anscnt + 1);
printf("%d\n", ans[k]);
return 0;
}
离谱。

浙公网安备 33010602011771号