poj 1742 多重背包变种 对时间要求特别严格

本文探讨了多重背包问题的多种解决方案,包括二进制拆分后的01背包、直接优化及混合完全背包和01背包的方法,并通过代码示例详细解释了每种方法的实现过程。

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

1 多重背包物品二进制拆分后01背包
#include <iostream>
#include <stdio.h>
#include <cstring>

using namespace std;


#define MAX_V (100+1)
#define MAX_F (100000+1)

int A[MAX_V],C[MAX_V];
int Value[MAX_F];
bool F[MAX_F];

int n,m;
#define printf //
int main()
{
	while(scanf("%d %d",&n,&m) != EOF)
	{
		if(n==0 && m== 0)
			return 0;

		for(int i=1;i<=n;i++)
		{

			scanf("%d",&A[i]);
			//cin>>A[i];
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&C[i]);
			//cin>>C[i];
		}

		int cnt = 0;
		//binary拆分物品
		//C数量 A价值
		for(int i=1;i<=n;i++)
		{
			printf("old:数量:%d 价值:%d\n",C[i],A[i]);
			int tc = C[i];
			for(int k=1;k<=tc; k <<= 1)
			{
				Value[cnt++] = A[i] *k;
				tc -= k;
				printf("\t\t %d:价值:%d \n",k,k*A[i]);

			}

			if(tc > 0)
			{
				Value[cnt++] = A[i] * tc;
				printf("\t\t%d:价值:%d\n",C[i],C[i]*A[i]);
			}
		}

		memset(F,0,sizeof(F));
		F[0]=true;
		for(int i=0;i<cnt;i++)
		{
			//for(int v=m;v>Value[i];v--)
			for(int v=m;v>=Value[i];v--)
			{
				if(F[v])
					continue;	
				if(F[v-Value[i]])
					F[v] = true;
				//F[v] = F[v] || F[v-Value[i]];

				printf("i=%d(%d) F[v-x]=%d F[%d]=%d\n",i,Value[i],F[v-Value[i]],v,F[v]);
//				if(F[v - Value[i] ] + value[i] > F[v])
//					F[v] = F[v - value[i] ] + value[i];
			}
		}



		int sums = 0;
		for(int v=m;v>0;v--)
			if(F[v])
			{
				++sums;
			}

		cout<<sums<<endl;



	}
}


 

2 按照之前网上说的思路尝试优化,但依然不理想,依然TLE.

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

using namespace std;


#define MAX_V (100+1)
#define MAX_F (100000+1)

int A[MAX_V],C[MAX_V];
int Value[MAX_F];
bool F[MAX_F]; //F[v]为使用i能否达到v

int n,m;
#define printf //
int main()
{
	while(scanf("%d %d",&n,&m) != EOF)
	{
		if(n==0 && m== 0)
			return 0;

		for(int i=1;i<=n;i++)
		{

			scanf("%d",&A[i]);
			//cin>>A[i];
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&C[i]);
			//cin>>C[i];
		}

		memset(F,0,sizeof(F));
		F[0]=true;
		int maxv = 0;
		int sum = 0;
		for(int i=1;i<=n;i++)
		{
			for(int v=maxv;v>=0;v--)
			{
				if(F[v])
				{
					for(int k=1;k<= C[i];k++)
					{
						int ki = v+ k*A[i];
						if(ki >m )
							break;
						if(F[ki])
							continue;
						++sum;
						F[ki]= true;

						if(ki > maxv)
							maxv = ki;
					}
					
				}

			}
		}



//		int sums = 0;
//		for(int v=m;v>0;v--)
//		{
//			printf("%d=%d\n",v,F[v]);
//			if(F[v])
//			{
//				++sums;
//			}
//		}

		cout<<sum<<endl;



	}
}

3 参考

http://blog.youkuaiyun.com/qingniaofy/article/details/7882407

混合 完全背包和01背包优化解决多重背包问题

如果一个物品的value*amount >= m时,则认为此物品可以取任意多件,因为在物品的件数限制amount内,一定可以满足m了。

#include <iostream>  
#include <stdio.h>  
#include <cstring>  
  
using namespace std;  
  
  
#define MAX_V (100+1)  
#define MAX_F (100000+1)  
  
int A[MAX_V],C[MAX_V];  
bool F[MAX_F];  
  
int n,m;  
#define printf //  
void ZeroOnePack(int value)
{
	printf("zero one pack value:%d\n",value);
	for(int v=m;v>=value;--v)
	{
		F[v] |= F[v-value];
		printf("F[%d] = %d\n",v,F[v]);
	}
}
void CompletePack(int value)
{
	for(int v=value;v<=m;v++) //完全背包和01背包只有循环顺序不同,具体可参考背包问题九讲
		F[v] |= F[v-value];
}
void MultiPack(int value,int amount)
{

	if(value * amount >= m)
	{

		CompletePack(value);
		return;
	} //这段对某些可以应用完全背包的物品,进行完全背包才最终AC的。
	int k=1;
	while(k<amount)
	{
		ZeroOnePack(value * k);
		amount -= k;
		k <<= 1;
	}
	if(amount)
		ZeroOnePack(value*amount);
}

int main()  
{  
    while(scanf("%d %d",&n,&m) != EOF)  
    {  
        if(n==0 && m== 0)  
            return 0;  
  

        for(int i=1;i<=n;i++)  
        {  
            scanf("%d",&A[i]);  
        }  
        for(int i=1;i<=n;i++)  
        {  
            scanf("%d",&C[i]);  
        }  
  
	memset(F,0,sizeof(F));
	F[0] = true;
	//数量c
        for(int i=1;i<=n;i++)  
        {  
		if(C[i])
		{
			MultiPack(A[i],C[i]);
		}
        }  
  
        int sums = 0;  
        for(int v=m;v>0;v--)  
            if(F[v])  
            {  
                ++sums;  
            }  
  
        cout<<sums<<endl;  
  
  
  
    }  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值