从零单排6

本文通过多个实例详细解析了01背包、完全背包及多重背包等问题,提供了丰富的代码模板,并介绍了二维背包、分组背包等进阶内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

d) 01背包

背包背包各种背包~!!!

表示今天那个计划应该是01背包

但一开始没注意就搞了01完全和多重

利用模版还是很轻松的~

再稍微搞一搞下周得准备unix考试了~

不过还是可以有很多时间做题目的~

今天适牛他们最后时刻被岛娘虐了,虽然他们并不认识我但是自己就和看比赛一样很鸡冻的关注着人人消息。。

加油~!


hdu 2602:http://acm.hdu.edu.cn/showproblem.php?pid=2602

/*
裸01背包
模版!
*/
#include<iostream>
#include<algorithm>
using namespace std;
int c[1005];
int w[1005];
int dp[1005];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int N,V;
		cin>>N>>V;
		for(int i=1;i<=N;i++)
		{
			cin>>w[i];//价值 
		}
		for(int i=1;i<=N;i++)
		{
			cin>>c[i];//费用 
		}
		int v;
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=N;i++)
		{
			for(v=V;v>=c[i];v--)
			{
				dp[v]=max(dp[v],dp[v-c[i]]+w[i]);
			}
		}
		cout<<dp[V]<<endl;
	}
	system("pause");
	return 0;
}

hdu 1114: http://acm.hdu.edu.cn/showproblem.php?pid=1114

/*
完全背包
要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,
这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。
*/
#include <iostream>
#include<algorithm>
#define INF 0x3f3f3f3f//无穷大 
using namespace std;
int dp[10005];
int main()
{
    int i,j,k,t,T;
    cin>>T;
    int from,to,n;
    while(T--)
    {
        memset(dp,INF,sizeof(dp));
        dp[0] = 0;
        cin>>from>>to>>n;
        int V = to - from;
        int v,w;
        for(i=0;i<n;i++)
        {
            cin>>v>>w;
            for(j=w;j<=V;j++)
            {
				dp[j]=min(dp[j - w] + v,dp[j]);
			}
        }
        if(dp[V]<INF)
            cout<<"The minimum amount of money in the piggy-bank is "<<dp[V]<<"."<<endl;
        else
            cout<<"This is impossible."<<endl;
    }
    return 0;
}

hdu 1171: http://acm.hdu.edu.cn/showproblem.php?pid=1171

/*
多重背包模版~!
从网上搜刮来的~
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[2000000],i,k,v;
void bag01(int cost,int weight)
{
 	for(i=v;i>=cost;i--)
  		dp[i]=max(dp[i-cost]+weight,dp[i]);
}
void complete(int cost,int weight)
{
	for(i=cost;i<=v;i++)
  		dp[i]=max(dp[i-cost]+weight,dp[i]);
}
void multiply(int cost,int weight,int amount)
{
 	if(cost*amount>=v)
  		complete(cost,weight);
	else
	{
  		k=1;
  		while(k<amount)
		{
   			bag01(k*cost,k*weight);
   			amount-=k;
   			k+=k;
  		}
  		bag01(cost*amount,weight*amount);
 	}
}
int main()
{
	int n,w[55],num[55],sum,j;
 	while(scanf("%d",&n),n>=0)
	{
  		sum=0;
  		memset(dp,0,sizeof(dp));
  		for(j=1;j<=n;j++)
		{
   			scanf("%d%d",&w[j],&num[j]);
   			sum+=w[j]*num[j];
  		}
  		v=sum/2;//这样dp[v]总是控制不大于sum/2
  		for(j=1;j<=n;j++)
   			multiply(w[j],w[j],num[j]);//对每一种多重背包,cost与weight等效
  		printf("%d %d\n",sum-dp[v],dp[v]);
 	}
 	return 0;
}

hdu 2844: http://acm.hdu.edu.cn/showproblem.php?pid=2844

/*
模版背包
以后复习到母函数用母函数来做 
*/#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<string.h>
#define MAX_VOLUMN 100001
#define MAX_N 11
#define max(a,b) ((a)>(b) ? (a) : (b))
using namespace std;
int vol, n;                                             // 背包容量 ;数量
int cv[MAX_VOLUMN], num[MAX_VOLUMN], f[MAX_VOLUMN];            
  
 
void ZeroOnePack(int cost, int value)                   //(01)体积、质量、费用;价值
{
     int v;
     for(v=vol; v>=cost; v--)
         f[v] = max(f[v], f[v-cost]+value);
}
  
 
void CompletePack(int cost, int value)                 //(无限)体积、质量、费用;价值
{
     int v;
     for(v=cost; v<=vol; v++)
         f[v] = max(f[v], f[v-cost]+value);
}
  
void  MultiplePack(int cost, int value, int amount)     //(多重)体积、质量、费用;价值;数量
{
     int k = 1;
     if(cost*amount >= vol)
         CompletePack(cost, value);
     else
     {
         while((amount-k) >= 0)
         { 
             ZeroOnePack(cost*k, value*k);
             amount -= k;
             k = k*2;
         }
         ZeroOnePack(cost*amount, value*amount);
     }
}
 
void dp()
{
     int i;
     for(i=1; i<=n; i++)
         MultiplePack(cv[i], cv[i], num[i]);
}
  
int  main()
{
	int i,k,j;
    while(scanf("%d %d", &n, &vol),n+vol)
    { 
        memset(f, 0, sizeof(f));
        for(i=1; i<=n; i++)
            scanf("%d",&cv[i]);
		for(i=1; i<=n; i++)
            scanf("%d",&num[i]);
        dp();
        int sum=0;
        for( i=1; i <= vol; i++ ) 
        {
        	if( f[i] == i ) //如果相等,则代表有这种情况 
            	sum++;   
        }  
        printf("%d\n", sum);                       //最大数
     }
     return 0;
}

hdu 1059: http://acm.hdu.edu.cn/showproblem.php?pid=1059

//多重背包
//HDU 1059
//题意:价值分别为1,2,3,4,5,6的物品的个数分别为 a[1],a[2],````a[6]
//问能不能分成两堆价值相等的
 
#include<stdio.h>
#include<string.h>
int a[7];
int f[120005];
int v,k;
void ZeroOnePack(int cost,int weight)//cost 为费用, weight 为价值 
{
    for(int i=v;i>=cost;i--)
       if(f[i-cost]+weight>f[i]) 
	   		f[i]=f[i-cost]+weight;
}    
void CompletePack(int cost,int weight)
{
    for(int i=cost;i<=v;i++)
        if(f[i-cost]+weight>f[i]) 
			f[i]=f[i-cost]+weight;
}         
void MultiplePack(int cost ,int weight,int amount)
{
    if(cost*amount>=v) 
		CompletePack(cost,weight);
    else
    {
        for(int k=1;k<amount;)
        {
            ZeroOnePack(k*cost,k*weight);
            amount-=k;
            k<<=1;
        }    
        ZeroOnePack(amount*cost,amount*weight);
    }    
}    
int main()
{
    int tol;
    int iCase=0;
    while(1)
    {
        iCase++;
        tol=0;
        for(int i=1;i<7;i++)
        {
            scanf("%d",&a[i]);
            tol+=a[i]*i;//总价值数 
        }  
        if(tol==0) 
			break;
        if(tol%2==1)
        {
            printf("Collection #%d:\nCan't be divided.\n\n",iCase);
            continue;
        }      
        else
        {
            v=tol/2;
            memset(f,0,sizeof(f));
            for(int i=1;i<7;i++)
              	MultiplePack(i,i,a[i]);
            if(f[v]==v) 
              	printf("Collection #%d:\nCan be divided.\n\n",iCase);
            else 
				printf("Collection #%d:\nCan't be divided.\n\n",iCase);
        }    
    }    
    return 0;    
}

hdu 2546: http://acm.hdu.edu.cn/showproblem.php?pid=2546

/*
模版01背包
要预留5元用来买最贵的菜
所以不妨先排序后把5元预支出来
然后对剩下的价格和钱做01背包
最后+5还原钱数减去最贵的菜和背包结果
*/
#include<iostream>
#include<algorithm>
using namespace std;
int f[1005];
int c[1005];
int v,k;
void ZeroOnePack(int cost,int weight)//cost 为费用, weight 为价值 
{
    for(int i=v;i>=cost;i--)
       if(f[i-cost]+weight>f[i]) 
	   		f[i]=f[i-cost]+weight;
}    
void CompletePack(int cost,int weight)
{
    for(int i=cost;i<=v;i++)
        if(f[i-cost]+weight>f[i]) 
			f[i]=f[i-cost]+weight;
}         
void MultiplePack(int cost ,int weight,int amount)
{
    if(cost*amount>=v) 
		CompletePack(cost,weight);
    else
    {
        for(int k=1;k<amount;)
        {
            ZeroOnePack(k*cost,k*weight);
            amount-=k;
            k<<=1;
        }    
        ZeroOnePack(amount*cost,amount*weight);
    }    
} 
int main()
{
	int n,money;
	while(cin>>n&&n!=0)
	{
		memset(c,0,sizeof(c));
		int max=0;
		for(int i=1;i<=n;i++)
		{
			cin>>c[i];
		}
		sort(c+1,c+n+1);
		cin>>v;
		memset(f,0,sizeof(f));
		if(v>=5)
		{
			max=c[n];
			v=v-5;
			for(int i=1;i<n;i++)
				ZeroOnePack(c[i],c[i]);
			cout<<v+5-f[v]-max<<endl;
		}
		else
		{
			cout<<v<<endl;
		}
	}
	system("pause");
	return 0;
}

hdu 2955: http://acm.hdu.edu.cn/showproblem.php?pid=2955

 /*
 又是01背包模版题
 被抓概率可以转换成安全概率,Roy的安全概率大于1-P时都是安全的。
 抢劫的金额为0时,肯定是安全的,所以d[0]=1;其他金额初始为最危险的所以概率全为0;
 */
 #include<iostream>
 #include<cstdio>
 #include<cmath>
 #define MAXN 101
 #define MAXV 10001
 using namespace std;
 int cost[MAXN];
int v,k;
 double weight[MAXV],f[MAXV];
  void ZeroOnePack(int cost,double weight)//cost 为费用, weight 为价值 
{
    for(int i=v;i>=cost;i--)
       if(f[i-cost]*weight>f[i]) 
	   		f[i]=f[i-cost]*weight;
}    
void CompletePack(int cost,int weight)
{
    for(int i=cost;i<=v;i++)
        if(f[i-cost]+weight>f[i]) 
			f[i]=f[i-cost]+weight;
}         
void MultiplePack(int cost ,int weight,int amount)
{
    if(cost*amount>=v) 
		CompletePack(cost,weight);
    else
    {
        for(int k=1;k<amount;)
        {
            ZeroOnePack(k*cost,k*weight);
            amount-=k;
            k<<=1;
        }    
        ZeroOnePack(amount*cost,amount*weight);
    }    
} 
 int main()
 {
     int T,n,i,j;
     double P;
     cin>>T;
     while(T--)
     {
         scanf("%lf %d",&P,&n);
         P=1-P;
         v=0;
         for(i=0;i<n;i++)
         {
             scanf("%d %lf",&cost[i],&weight[i]);
             weight[i]=1-weight[i];
             v+=cost[i];
         }
         memset(f,0,sizeof(f));;
         f[0]=1;
         for(i=0;i<n;i++)
         {
			ZeroOnePack(cost[i],weight[i]);
		}
         bool flag=false;
         for(i=v;i>=0;i--)
         {
             if(f[i]-P>0.000000001)
             {
                 printf("%d\n",i);
                 break;
             }
         }
     }
     return 0;
 }

hdu 1203:http://acm.hdu.edu.cn/showproblem.php?pid=1203

/*
背包。。
搞笑。。。
因为n!=0&&m!=0导致wa了三次。。。
总费用看作背包容量,申请费用看作每个背包的重量,价值为至少有一份offer的概率
另外减法原理 
   状态转移方程:dp[j]=max(dp[j] , 1-(1-dp[j-a[i].val])*(1-a[i].p));
*/
#include<iostream>
#include<cstdio>
using namespace std;
int c[10005];
double v[10005];
double f[10005];
int n;
int m;
void ZeroOnePack(int cost,double weight)//cost 为费用, weight 为价值 
{
    for(int i=n;i>=cost;i--)
       if(1-(1-f[i-cost])*(1-weight)>f[i]) 
	   		f[i]=1-(1-f[i-cost])*(1-weight);
}    
int main()
{
	while(cin>>n>>m,n!=0||m!=0)
	{
		memset(f,0,sizeof(f));
		for(int i=0;i<m;i++)
		{
			cin>>c[i]>>v[i];
		}
		for(int i=0;i<m;i++)
		{
			ZeroOnePack(c[i],v[i]);
		}
		printf("%.1lf%%\n",f[n]*100);
	}
	system("pause");
	return 0;
}


去整理一下物理实验考试的资料...今晚先不做题了~!

下一次是RMQ问题~!

PS:今天无意看到一份背包问题的题单。。各种不会尼玛。。。二维背包分组背包什么的。。。mark下学习学习。。

hdu 2159:http://acm.hdu.edu.cn/showproblem.php?pid=2159

/*
二维背包。。没有做过。。
学习下
二维费用的背包问题是指:对于每件物品,具有两种不同的费用;
选择这件物品必须同时付出这两种代价;对于每种代价都有 一个可付出的最大值(背包容量)。
问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,
第i件物品所需的两种代价分别为a[i]和 b[i]。
两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i]。

费用加了一维,只需状态也加一维即可。
设f[v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。状态转移方程就是:

f[v][u]=max{f[i-1][v][u],f[v-a[i]][u-b[i]]+w[i]}

如前述方法,可以只使用二维的数组:当每件物品只可以取一次时变量v和u采用逆序的循环,
当物品有如完全背包问题时采用顺序的循环。当物品有如多重背包问题时拆分物品。
*/
 
#include<iostream>
#include<algorithm>
using namespace std;
#define N 105
int dp[N][N];//二维的 
int a[N],b[N];
int main()
{
    int n,m,k,s,l;
    while(cin>>n>>m>>k>>s)
    {
        for(int i=0;i<k;i++)
    		cin>>a[i]>>b[i];  //得到的经验值和会减掉的忍耐度
        memset(dp,0,sizeof(dp));
        for(int i=0;i<k;i++)  //杀怪的循环 
        { 
            for(int j=1;j<=s;j++)  //限制条件,最多的杀怪数s
            {
                for(int l=b[i];l<=m;l++)  //二维背包问题的第二个for循环
                	dp[j][l]=max(dp[j][l],dp[j-1][l-b[i]]+a[i]); //dp[j][l]它表示 用掉了l点的忍耐度,并且杀了j个怪后,所获得的最大经验数
            }
        }
        int i;
        for(i=0;i<=m;i++)   //i之所以从0开始到m是由于dp[j][l]它表示 用掉了l点的忍耐度,并且杀了j个怪后,所获得的最大经验数
        	if(dp[s][i]>=n) 
				break;  //即用掉了m点的忍耐度,并且杀了s只怪兽后,所获得的最大经验数
        if(dp[s][i]<n)   //break即跳出后还小于升级所需经验值时。。。 
        	cout<<"-1"<<endl; //即如果在杀掉s只怪兽后所得的经验值小于还需要的经验值的话就无法升级输出-1  
        else
        	cout<<m-i<<endl; ////在杀了s只怪兽后,用掉了i大小的忍耐度,则还能保留的最大忍耐度是m-i
    }
    return 0;
}

hdu 2639: http://acm.hdu.edu.cn/showproblem.php?pid=2639

/*
01背包,第K优决策
题解来自网络,又是一个新知识点啊。。。
具体内容回去好好看背包九讲。。
*/
#include<iostream>
using namespace std;

int n,vol,k;
int dp[1005][35],value[105],cost[105],A[35],B[35];

void kth_ZeroOnePack()
{
	memset(dp,0,sizeof(dp));

	int i,j,kk,a,b,c;
	for(i=1;i<=n;++i)
	{
		for(j=vol;j>=cost[i];--j)
		{
			for(kk=1;kk<=k;++kk)
			{
				A[kk]=dp[j-cost[i]][kk]+value[i];
				B[kk]=dp[j][kk];
			}

			A[kk]=B[kk]=-1;
			a=b=c=1;
			while(c<=k && (A[a]!=-1 || B[b]!=-1))
			{
				if(A[a]>B[b])
					dp[j][c]=A[a],++a;
				else
					dp[j][c]=B[b],++b;

				if(dp[j][c]!=dp[j][c-1])
					++c;
			}
		}
	}

	cout<<dp[vol][k]<<endl;
}
		
int main()
{
	int tCase;

	cin>>tCase;
	while(tCase--)
	{
		cin>>n>>vol>>k;

		for(int i=1;i<=n;++i)
			cin>>value[i];
		for(int j=1;j<=n;++j)
			cin>>cost[j];

		kth_ZeroOnePack();
	}

	return 0;
}

hdu 1712: http://acm.hdu.edu.cn/showproblem.php?pid=1712

/*
分组背包。。。
又是不会的知识点。。
背包九讲~!
*/
#include <iostream>
using namespace std;
int a[101][101],f[101]; 
int main()
{
    int n,m,i,j,k;
    while(cin >> n >> m && (n != 0 || m != 0))
    {
    	memset(f,0,sizeof(f));
        for(i = 1;i <= n;i++)
            for(j = 1;j <= m;j++)
            	cin >> a[i][j];
        for(i = 1;i <= n;i++) //第一重循环:分组数
            for(j = m;j >= 0;j--) //第二重循环:容量体积
                for(k = 0;k <= j;k++) //第三重循环:属于i组的k
                    f[j] = max(f[j],f[j-k]+a[i][k]);
        cout << f[m] << endl;
    }
    system("pause");
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值