AcWing 11. 求01背包最优选法的方案数

题目链接:点击这里
在这里插入图片描述
在这里插入图片描述

参考链接:https://www.acwing.com/solution/acwing/content/6135/

f [ j ] f[j] 表示背包容积”恰好”为 j j 时的最大价值和

g [ j ] g[j] 表示背包容积”恰好”为 j j 时取最优解的方案数(两个数组通过下标 j j 相对应)

最后,找到最优解的数值,在 g [ j ] g[j] 里面只要与这个数相等的都是最优方案数

注意:因为最终不一定占满全部的背包体积,所以最优解不一定是f[m]

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 1010, mod = 1e9+7, INF = 1000000;
int n, m;
int f[N], g[N];

int main()
{
    cin>>n>>m;
    
    g[0] = 1;		//啥都不选也算一种方案
    
	for(int i = 1; i <= m; ++i)		//恰好
    	f[i] = -INF;
    
    for(int i = 0; i < n; i++)
	{
        int v, w;
        cin>>v>>w;
        
        for(int j = m; j >= v; --j)
        {
        	int t = max(f[j], f[j-v]+w);
        	int s = 0;
        	if(t==f[j])	s += g[j];			//不选新物品 
        	if(t==f[j-v]+w)	s += g[j-v];	//选新物品 
        	if(s >= mod)	s -= mod;
        	f[j] = t;
        	g[j] = s;
		}
    }
    
    int maxw = 0;
    for(int i = 0; i <= m; ++i)
    	maxw = max(maxw, f[i]);
	
	int ans = 0;
	for(int i = 0; i <= m; ++i)
	{
		if(maxw==f[i])
		{
			ans += g[i];
			if(ans>=mod)	ans -= mod;
		} 
	}
    
    cout<<ans<<endl;
    
    return 0;
}

参考链接:https://www.acwing.com/solution/acwing/content/3999/

f [ i ] f[i] 用来存储背包容积为 i i 时的最佳方案的总价值,

c n t [ i ] cnt[i] 为背包容积为 i i 时总价值为最佳的方案数

先初始化所有的 c n t [ i ] cnt[i] 1 1 ,因为背包里什么也不装也是一种方案

外层循环 n n 次,每次读入新物品的 v , w v,w

求出装新物品时的总价值,与不装新物品时作对比

如果装新物品的方案总价值更大,那么用 f [ j v ] + w f[j−v]+w 来更新 f [ j ] f[j] ,用 c n t [ j v ] cnt[j−v] 更新 c n t [ j ] cnt[j]

如果总价值相等,那么最大价值的方案数就多了 c n t [ j v ] cnt[j−v]

#include<iostream>

using namespace std;

const int N = 1010;
const int mod = 1e9 + 7;

int f[N], cnt[N];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    
    for(int i = 0; i <= m; i ++)
		cnt[i] = 1;

    for(int i = 1; i <= n; i ++)
    {
        int v, w;
        scanf("%d%d", &v, &w);
        
        for(int j = m; j >= v; j --)
        {
            int value = f[j - v] + w;	//用新物品时的最大价值
            if(value > f[j])			//更新f[j]和g[j]
            {
                f[j] = value;
                cnt[j] = cnt[j - v];
            }
			else if(value == f[j]) //用新物品和不用新物品的最佳方案价值相等时,方案个数累加上cnt[j-v]
			{
                cnt[j] = (cnt[j] + cnt[j - v]) % mod;
            }
        }
    }

    printf("%d", cnt[m]);
    return 0;
}

补充:求总方案数。

对于这类改变问法的问题,一般只需将状态转移方程中的 m a x max 改成 s u m sum 即可。例如若每件物品均是完全背包中的物品,转移方程即为
F [ i , v ] = s u m { F [ i 1 , v ] , F [ i , v C i ] } F [i, v] = sum\left\{F [i − 1, v], F [i, v − C_i]\right\}
初始条件是 F [ 0 , 0 ] = 1 F [0, 0] = 1

事实上,这样做可行的原因在于状态转移方程已经考察了所有可能的背包组成方案。

发布了748 篇原创文章 · 获赞 113 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/104384853