背包问题系列——背包问题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\)大的价值怎么算呢?我们来看一下完全背包是怎么算的:

\[dp[j] = \begin{cases} dp[j] & 没有取任何物品\\ dp[j - v[i]] + w[i] & 取了第i个物品\end{cases} \]

然后因为我们要求前\(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; 
}

离谱。

posted @ 2021-08-06 17:56  Akafuyu  阅读(79)  评论(0)    收藏  举报