01背包与完全背包问题

一篇不错的讲解背包问题的LeetCode题解

https://leetcode-cn.com/problems/coin-change/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-sq9n/

介绍01背包与完全背包的思想与一些应用

一.01背包
/*
10 5
3 5
2 3
4 4
6 10
5 8
*/
/*
//方法一: 
#include <stdio.h>
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
	int m, n;
	int a[100][100], w[100], v[100];
	
	cin >> n >> m;
	for(int i = 1; i <= m; ++i)
	{
		cin >> w[i] >> v[i];
	}
	
	for(int i = 0; i <= n; ++i)
	{
		a[0][i] = 0;
	}
	for(int i = 0; i <= m; ++i)
	{
		a[i][0] = 0;
	}
	
	for(int i = 1; i <= m; ++i)
	{
		for(int j = 0; j <= n; ++j)
		{
			if(j < w[i])
			{
				a[i][j] = a[i-1][j];
			}
			else 
			{
				a[i][j] = max(a[i-1][j], a[i-1][j-w[i]]+v[i]);
			}
		}
	}
	
	cout << a[m][n] << endl;
	
	return 0;
}
*/

/* 
2 5 
2 2 
5 1 
*/

#include <stdio.h>
#include <iostream>
#include <string.h>

using namespace std;

int main()
{
	int n, m;
	int f[100], w[100], v[100];
	
	cin >> m >> n;
	
	for(int i = 1; i <= m; ++i)
	{
		cin >> w[i] >> v[i];
	}
	//为了实现背包装满,先把所有值赋值为一个极小的值,然后f[0] = 0; 
//	memset(f, -999999, sizeof(f));
//	f[0] = 0; 
	
	//如果不用装满	
	memset(f, 0, sizeof(f));
	
	for(int i = 1; i <= m; ++i)
	{
		for(int j = n; j >= w[i]; --j)
		{
			f[j] = max(f[j], f[j-w[i]]+v[i]);
		}
	}
	
	cout << f[n] << endl;
	
	return 0;
}

二.完全背包

/*
初始化的细节问题:
有的问题中有要求“恰好装满背包”,有的则没有要求,这就在初始化DP数组时有所不同。
恰好装满背包:则初始化时,DP[0]=0, 其他的DP[1…..V]均设为负的无穷大。
无须恰好装满背包:则初始化时,DP[1…..V]全部设为0;
*/
/*
描述
直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。本题要求是背包恰好装满背包时,求出最大价值总和是多少。如果不能恰好装满背包,输出NO

输入
第一行: N 表示有多少组测试数据(N<7)。 
接下来每组测试数据的第一行有两个整数M,V。 M表示物品种类的数目,V表示背包的总容量。(0<M<=2000,0<V<=50000)
接下来的M行每行有两个整数c,w分别表示每种物品的重量和价值(0<c<100000,0<w<100000)
输出
对应每组测试数据输出结果(如果能恰好装满背包,输出装满背包时背包内物品的最大价值总和。 如果不能恰好装满背包,输出NO)

2
1 5
2 2
2 5
2 2
5 1
*/
#include <stdio.h>
#include <iostream>
#include <string.h>

using namespace std;

int main()
{
	int n, m;
	int c[100], w[100];
	int f[100];
	
	int num;
	cin >> num;
	
	while(num--)
	{
		cin >> m >> n;
		for(int i = 1; i <= m; ++i)
		{
			cin >> c[i] >> w[i];
		}
		
		//为了实现背包装满,先把所有值赋值为一个极小的值,然后f[0] = 0; 
		
		memset(f, -999999, sizeof(f)); 
		f[0] = 0;
		
		//如果不用装满
		/*
		memset(f, 0, sizeof(f));
		*/
		for(int i = 1; i <= m; ++i)
		{
			for(int j = c[i]; j <= n; ++j)
			{
				f[j] = max(f[j], f[j-c[i]]+w[i]);
			}
		}
		
		if(f[n] > 0)
		{
			cout << f[n] << endl;
		}
		else
		{
			cout << "NO!" << endl;
		}
	}
	
	return 0;
}
/*
标题:包子凑数

小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。

每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。

当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。

小明想知道一共有多少种数目是包子大叔凑不出来的。

输入
----
第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)  

输出
----
一个整数代表答案。如果凑不出的数目有无限多个,输出INF。

例如,
输入:
2  
4  
5   

程序应该输出:
6  

再例如,
输入:
2  
4  
6    

程序应该输出:
INF

样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。  
对于样例2,所有奇数都凑不出来,所以有无限多个。  

*/

#include <stdio.h>
#include <iostream>

using namespace std;

int f(int m, int n)
{
	if(n == 0) return m;
	else
	{
		return f(n, m%n);
	}
}

int main()
{
	int n;
	int arr[101], b[10001] = {0};
	
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> arr[i];
	}
	
	int flag = arr[1];
	for(int i = 2; i <= n; ++i)
	{
		flag = f(flag, arr[i]);
	}
	
	if(flag != 1)
	{
		cout << "INF";
	}
	else
	{
		b[0] = true;
		for(int i = 1; i <= n; ++i)
		{
			for(int j = arr[i]; j <= 100*100; ++j)
			{
				if(b[j-arr[i]])
				{
					b[j] = true;
				}
			}
		}
		
		int count = 0;
		for(int i = 1; i <= 10000; ++i)
		{
			if(!b[i])
			{
				++count;
			}
		}
		
		cout << count;
	}
	
	return 0;
} 
题目:
L3-001. 凑零钱
时间限制 
200 ms
内存限制 
65536 kB
代码长度限制 
8000 B
判题程序 
Standard 
作者 
陈越
韩梅梅喜欢满宇宙到处逛街。现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债。韩梅梅手边有104枚来自各个星球的硬币,需要请你帮她盘算一下,是否可能精确凑出要付的款额。
输入格式: 
输入第一行给出两个正整数:N(<=104)是硬币的总个数,M(<=102)是韩梅梅要付的款额。第二行给出N枚硬币的正整数面值。数字间以空格分隔。
输出格式: 
在一行中输出硬币的面值 V1 <= V2 <= ... <= Vk,满足条件 V1 + V2 + ... + Vk = M。数字间以1个空格分隔,行首尾不得有多余空格。若解不唯一,则输出最小序列。若无解,则输出“No Solution”。 
注:我们说序列{A[1], A[2], ...}比{B[1], B[2], ...}“小”,是指存在 k >= 1 使得 A[i]=B[i] 对所有 i < k 成立,并且 A[k] < B[k]。 
输入样例1:
8 9
5 9 8 7 2 3 4 1
输出样例1:
1 3 5
输入样例2:
4 8
7 2 4 3
输出样例2:
No Solution

以下列出三种可解方法
#include <stdio.h>
#include <iostream>
#include <vector>
#include <string.h>
#include <algorithm>
#include <queue>
#include <set>

using namespace std;

bool cmp(int a, int b)
{
	return a > b;
}

int main()
{	
	int n, m;
	bool f[10001];
	bool flag[10001][101] = {0};
	cin >> n >> m;
	
	int w[10001];
	for(int i = 1; i <= n; ++i)
	{
		cin >> w[i];
	}
	
	sort(w+1, w+n+1, cmp);
	f[0] = 1;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = m; j >= w[i]; --j)
		{
			if(f[j-w[i]])
			{
				flag[i][j] = 1;
				f[j] = 1;
			}
		}
	}
	
	vector<int> v;
	if(f[m] == 0)
	{
		cout << "No Solution" << endl;
		return 0;
	}
	else
	{
		int a = n, b = m;
		while(b)
		{
			if(flag[a][b])
			{
				v.push_back(w[a]);
				b -= w[a];
			}
			--a;
		}
	}
	
	for(int i = 0; i < v.size()-1; ++i)
	{
		cout << v[i] << " ";
	}
	cout << v[v.size()-1];
	
	return 0;
} 


#include <stdio.h>
#include <iostream>
#include <vector>
#include <string.h>
#include <algorithm>
#include <queue>
#include <set>

using namespace std;

bool cmp(int a, int b)
{
	return a > b;
}

int main()
{	
	int n, m;
	int f[10001];
	bool flag[10001][101] = {0};
	cin >> n >> m;
	
	int w[10001];
	for(int i = 1; i <= n; ++i)
	{
		cin >> w[i];
	}
	
	sort(w+1, w+n+1, cmp);
	f[0] = 0;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = m; j >= w[i]; --j)
		{
			if(f[j] <= f[j-w[i]]+w[i])
			{
				flag[i][j] = 1;
				f[j] = f[j-w[i]]+w[i];
			}
		}
	}
	
	vector<int> v;
	if(f[m] != m)
	{
		cout << "No Solution" << endl;
		return 0;
	}
	else
	{
		int a = n, b = m;
		while(b)
		{
			if(flag[a][b])
			{
				v.push_back(w[a]);
				b -= w[a];
			}
			--a;
		}
	}
	
	for(int i = 0; i < v.size()-1; ++i)
	{
		cout << v[i] << " ";
	}
	cout << v[v.size()-1];
	
	return 0;
} 


第三种不能的全部分数,原因好像是数组f的大小问题,提示段错误,此坑以后再补,注意用memset初始化数组的条件
#include <stdio.h>  
#include <iostream>  
#include <vector>  
#include <string.h>  
#include <algorithm>  
#include <queue>  
#include <set>  
  
using namespace std;  
  
bool cmp(int a, int b)  
{  
    return a > b;  
}  
  
int main()  
{     
    int n, m;  
    vector<int> f(10010, -99999);  
    bool flag[10001][101] = {0};  
    cin >> n >> m;  
      
    int w[10001];  
    for(int i = 1; i <= n; ++i)  
    {  
        cin >> w[i];  
    }  
      
    sort(w+1, w+n+1, cmp);  
    f[0] = 0;  
    for(int i = 1; i <= n; ++i)  
    {  
        for(int j = m; j >= w[i]; --j)  
        {  
            if(f[j] <= f[j-w[i]]+w[i])  
            {  
                flag[i][j] = 1;  
                f[j] = f[j-w[i]]+w[i];  
            }  
        }  
    }  
      
    vector<int> v;  
    if(f[m] == 0)  
    {  
        cout << "No Solution" << endl;  
        return 0;  
    }  
    else  
    {  
        int a = n, b = m;  
        while(b)  
        {  
            if(flag[a][b])  
            {  
                v.push_back(w[a]);  
                b -= w[a];  
            }  
            --a;  
        }  
    }  
      
    for(int i = 0; i < v.size()-1; ++i)  
    {  
        cout << v[i] << " ";  
    }  
    cout << v[v.size()-1];  
      
    return 0;  
}   

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值