C-Accepted Necklace(01背包的降维)

题目地址:https://vjudge.net/contest/422351#problem/C

题目描述:

 I have N precious stones, and plan to use K of them to make a necklace for my mother, but she won't accept a necklace which is too heavy. Given the value and the weight of each precious stone, please help me find out the most valuable necklace my mother will accept.

Input
The first line of input is the number of cases.

For each case, the first line contains two integers N (N <= 20), the total number of stones, and K (K <= N), the exact number of stones to make a necklace.

Then N lines follow, each containing two integers: a (a<=1000), representing the value of each precious stone, and b (b<=1000), its weight.

The last line of each case contains an integer W, the maximum weight my mother will accept, W <= 1000.

Output
For each case, output the highest possible value of the necklace.

题目大意:

在N颗宝石中选择K颗为母亲制作项链,每颗宝石有两个数据,重量和价值,母亲只会接受重量和小于W的项链串,请帮助我找出母亲会接受的最有价值的项链。

Sample Input
1
2 1
1 1
1 1
3

Sample Output
1

 一、题目简述:

这道题有着单独拿出来讲的价值,首先这应当是一道01背包的题目,然后由于数据范围非常小,所以可以作为一道dfs的题目去解决。

二、dfs做法:

2-1:思路:

对于每一个项链都有选和不选两种方案,当做到最后一个决策的时候,将当前最大价值和当前搜索的方案价值进行比较。

2-2:剪枝:

1.当前选择重量大于W时剪枝。

2.当前选择数量大于K时剪枝。

2-3.代码:

inline void dfs(int nlim,int num,int w,int p)
{
    if(w>W) return ;
    if(num>k) return ; 
    if(nlim==n)
    {
        if(p>pmax)pmax=p;
        return ;
    }
    dfs(nlim+1,num+1,w+b[nlim+1],p+a[nlim+1]);//选择下一个
    dfs(nlim+1,num,w,p);//不选择下一个 
}

三、dp做法:

3-1:思路:

这是一个比较常规的二维01背包问题,相比于01背包问题,它多了一个限制条件即最大数量为K,我们可以用dp[i][j][k]来表示对第i个物品进行决策的时候,选取重量为j并且已经选取k个的最佳决策,那么可以得到其状态转移方程为dp[i][j][l]=max(dp[i-1][j][l],dp[i-1][j-b[i]][l-1]+a[i])。

然后就是循环体的构建问题,显然我们应当会用到三层循环,最外层的循环是物品的循环是毫无疑问的,第二层和第三层的循环应当分别是重量j从0到W的循环和l从1到k的循环。这是由于倘若将顺序调换,会造成某个宝石可能被多次选择,而那就变成了另一种背包问题。

细节:当j的空间小于b[i]的时候,因当特判,因为这个时候的dp[i][j][l]只能来自于dp[i-1][j][l]。

int main()
{
    read(T);
    while(T--)
    {
        read(n);read(k);
        for(int i=1;i<=n;i++){read(a[i]);read(b[i]);}
        read(W);
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=W;j++)
            {
                for(int l=1;l<=k;l++)
                {
                    if(j<b[i]) dp[i][j][l]=dp[i-1][j][l];
                    else dp[i][j][l]=max(dp[i-1][j][l],dp[i-1][j-b[i]][l-1]+a[i]);
                }
            }
        }
        printf("%d\n",dp[n][W][k]);
        memset(dp,0,sizeof(dp));
    }
    return 0;

 

3-2:降维:

我们先来讨论01背包的降维问题,将题目中的个数限制去除,变成如下形式:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);

通过以上式子可以看出,我们对当前的状态进行更新时,只依赖于上一层所记录的状态,因此可以使用滚动数组的思想,将n行变为2行。

这个时候的空间已然被压缩的很小了,然后倘若我们删除这一行从后向前枚举重量,那么每次更新的时候dp[j-b[i]]所保存的仍旧是上一次更新的状态,由此我们就可以得到一维的状态转移方程:

for(int j=W;j>=b[i];j--) dp[j]=max(dp[j],dp[j-b[i]]);

将其应用到二维01背包问题中。

3-3代码:

    while(T--)
    {
        read(n);read(k);
        for(int i=1;i<=n;i++){read(a[i]);read(b[i]);}
        read(W);
        for(int i=1;i<=n;i++)
        {
            for(int j=W;j>=b[i];j--)
            {
                for(int l=1;l<=k;l++)
                {
                    dp[j][l]=max(dp[j][l],dp[j-b[i]][l-1]+a[i]);
                }
            }
        }
        printf("%d\n",dp[W][k]);
        memset(dp,0,sizeof(dp));
    }

这就是本题的dp解法。

posted @ 2021-02-19 19:16  ztlsw  阅读(107)  评论(0)    收藏  举报