蓝桥杯算法练习笔记(14)__背包问题练习

本文章基于《计蒜客蓝桥杯省赛2019训练营》整理记录,并由个人对部分内容进行补充说明。文章仅用于个人学习交流使用。

1.蒜头君的购物袋1

在这里插入图片描述

//蒜头君的购物袋 
#include<iostream>
#include<cstring> 
using namespace std;

int max(int x,int y){
	return x>y?x:y;
}

int main(){
	
	int n,v;
	cin>>n>>v;
	int a[n];
	int dp[v+1];
	memset(dp,0,sizeof(dp));
	for(int i=0;i<n;i++){
		cin>>a[i];
		for(int j=v;j>=a[i];j--){
			dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
		}
	}
	cout<<v-dp[v]<<endl;
	return 0;
} 
2.蒜头君的存钱罐

在这里插入图片描述
在这里插入图片描述

//存钱罐 
#include<iostream>
#include<cstring>
using namespace std;

const int N=1e4+5;
const int inf=0x3f3f3f3f;//无穷大量 

int f[N];

int main(){
	memset(f,inf,sizeof(f));
	f[0]=0;
	int a,b;
	cin>>a>>b;
	int v=b-a;
	int n;
	cin>>n;
	int x,y;
	for(int i=0;i<n;i++){
		cin>>x>>y;
		for(int j=y;j<=v;j++){
			f[j]=min(f[j],f[j-y]+a);
		}
	}
	if(f[v]==inf){
		cout<<"no"<<endl;
	}else{
		cout<<f[v]<<endl;
	}
	return 0;
	
}
/*
--cin:
1 6
2
10 3
20 4
--cout:
no
*/

3.平分娃娃

在这里插入图片描述

//平分娃娃
#include<iostream>
using namespace std;

int f[200010],a[10];

int main() {
	int sum=0;
	for(int i=1; i<=6; i++) {
		cin>>a[i];
		sum+=a[i]*i;
	}
	if(sum&1) { //奇数
		cout<<"no"<<endl;
		return 0;
	}
	sum>>=1; //除以二
	for(int i=1; i<=6; i++) { //多重背包
		int k;
		for(k=1; k*2<1+a[i]; k<<=1) { //二进制枚举 ( 能够优化时间)
			for(int j=sum; j>=i*k; j--) {
				f[j]=max(f[j],f[j-i*k]+i*k);
			}
		}
		k=a[i]-k+1;
		for(int j=sum; j>=i*k; --j) {
			f[j]=max(f[j],f[j-i*k]+i*k);
		}

	}
	if(sum==f[sum]) {
		cout<<"yes"<<endl;
	}

	return 0;
}
/*
--cin:
2 2 2 2 2 2
--cout:
yes
*/

4.等和的分割子集

在这里插入图片描述

//等和的分割子集
#include<iostream>
using namespace std;
long long dp[4000];
int main(){
	int n;
	cin>>n;
	int sum=n*(n+1)/2;
	dp[0]=1;
	for(int i=1;i<=n;i++){
		for(int j=sum;j>=i;j--){
			dp[j]+=dp[j-i];
		}
	} 
	if(sum%2==1){
		cout<<"0"<<endl;
	}else{
		cout<<dp[sum/2]/2<<endl;
	}
} 
/*
--cin:
7
--cout:
4
*/
5.饭卡

在这里插入图片描述
在这里插入图片描述

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

const int N=2e3+9;
int a[1010],f[N];
int main(){
	int n,m;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	cin>>m;
	memset(f,0,sizeof(f));
	f[0]=1;
	sort(a,a+n);
	int ma=0;
	for(int i=0;i<n;i++){
		for(int j=m-5;j>=0;j--){
			if(f[j]){
				f[j+a[i]]=1;
				ma=max(ma,j+a[i]);
			}
		}
	}
	cout<<m-ma<<endl;
	
	return 0;
}
6.整数划分

在这里插入图片描述
在这里插入图片描述

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

const int N=1e3+9;
const int mod=1e9+9;

int f[N];
int main() {

	int t;
	cin>>t;
	while(t--) {
		memset(f,0,sizeof f);
		f[0]=1;
		int n;
		cin>>n;
		for(int i=1; i<=n; i++) {
			for(int j=i; j<=n; j++) {
				(f[j]+=f[j-i])%=mod;
			}
		}
		cout<<f[n]<<endl;
	}
	return 0;
}
/*
--cin:
1 6
--cout:
11
*/
7.蒜头君的offer

在这里插入图片描述
在这里插入图片描述

//offer
#include<iostream>
using namespace std;
const int N=1e4+9;
double f[N];
int main(){
	int n,m;
	cin>>n>>m;
	int x;
	double y;
	while(m--){
		cin>>x>>y;
		for(int j=n;j>=x;j--){
			f[j]=max(f[j],1.0-(1-f[j-x])*(1-y));
		}
	}
	printf("%.1lf%%\n",f[n]*100); 
	return 0;
}
/*
--cin:
10 3
4 0.1
4 0.2
5 0.3
--cout:
44.0%
*/
8.新年趣事之打牌

在这里插入图片描述
在这里插入图片描述

//过年趣事之打牌 
#include<iostream>
using namespace std;

const int N=1e5+9;
int f[N]={0},path[N],a[110];
int n,total,sum=0,res;

void print(int url){
	if(url){
		print(url-a[path[url]]);
		if(url==res){
			cout<<path[url]<<endl;
		}else{
			cout<<path[url]<<" ";
		}
	}
} 

int main(){
	cin>>total>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	f[0]=1;
	res=sum-total;
	for(int i=1;i<=n;i++){
		for(int j=res;j>=a[i];j--){
			f[j]+=f[j-a[i]];
			if(f[j]>100){
				f[j]=10;
			}
			if(!path[j]&&f[j-a[i]]){
				path[j]=i;
			}
		}
	}
	if(!f[res]){
		cout<<0<<endl;
	}else if(f[res]>1){
		cout<<-1<<endl; 
	}else{
		print(res);
	}
	return 0;
}

9.猫狗大战

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

const int N=209;
int a[N],f[N][N*20];

int main(){
	int n,sum=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=n/2+1;j>=0;j--){
			for(int k=sum/2;k>=0;k--){
				if(f[j][k]){
					f[j+1][k+a[i]]=1;
				}
			}
		}
	}
	for(int i=sum/2;i>=0;--i){
		if(f[n/2][i] || f[n/2+1][i] && n & 1 ){
			cout<<i<<" "<<sum-i<<endl;
			break;
		}
	}
	return 0;
}
10.搭建双塔

在这里插入图片描述

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

int f[110][2003],h[110];

int main(){
	int n,sum=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>h[i];
	}
	memset(f,-1,sizeof f);
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		sum+=h[i];
		for(int j=0;j<=sum;j++){
			if(f[i-1][j]>f[i][j]){
				f[i][j]=f[i-1][j];
			}
			if(j>=h[i]&&f[i-1][j-h[i]]>=0){
				f[i][j]=max(f[i][j],f[i-1][j-h[i]]);
			}
			if(f[i-1][j+h[i]]>=0){
				f[i][j]=max(f[i][j],f[i-1][j+h[i]]+h[i]);
			}
			if(h[i]>=j && f[i-1][h[i]-j]>=0){
				f[i][j]=max(f[i][j],f[i-1][h[i]-j]+h[i]-j);
			}
		}
	}
	if(f[n][0]){
		cout<<f[n][0]<<endl;
	}else{
		cout<<"no"<<endl;
	}
	return 0;
}
/*
--cin:
5
1 3 4 5 2
--cout: 
7 
*/
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值