hdu 2639 Bone Collector II

题目 

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

分析

     求01背包计算过程中产生的所有可能值从大到小排序后的第K大值(即包含所有不满足条件,但是能够计算出来的结果,当时忽略此条件卡了许久),本题普通解决思路为:用一个数组或者栈记录01背包过程中所有可能产生的值,然后排序,去重复,在求第K大的值(这种方法就是”暴搜“,参考 http://blog.csdn.net/speedcell4/article/details/8689278)     比较好的做法是在用滚动数组处理01背包(状态转移方程:f[v]=max{f[v],f[v-cost]+weight})的时候,记录每一个资源限制下的前K个最大值(转移方程改进为:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}),然后进行归并排序,找到合并max括号中两个状态各自的前K个最大值之后的前K个最大值,并付给f[i]状态。
    在进行合并的时候也存在两种方案:第一种、先将两个状态下的前K个值放到一个长度为2K的数组中,然后利用sort函数进行去重和排序,最后将结果赋值给f[i];第二种、利用合并排序的思想找两个类似指针的东西分别指向两个状态的前K个值得数组,然后遍历找到合并后的前K个值,不过此时应该注意题目要求是找到去掉重复之后的第K大的值,所以循环中应该判断相邻的两个值是否相同。

复杂度分析

    合并这两个有序队列并将结果的前K项储存到f[i][v][1..K]中的复杂度是O(K),因此最后的答案存储在f[N][V][K],总的时间复杂度复杂度是O(VNK),空间复杂度是O(VK)

代码

#include <iostream>
using namespace std;
#define max2963(a,b) (a>b?a:b)
long f2963[1001][31];
long pre2963[31],cur2963[31];
int T2963,N2963,V2963,K2963,v2963[1000];
void ZeroOnePack2963(int cost,int weight)
{
	for(int i=V2963;i>=weight;--i)
	{
		int j;
		for(j=1;j<=K2963;++j)//保存转移方程右边两个状态各自的前K个最大值
		{
			pre2963[j]=f2963[i-weight][j]+cost;
			cur2963[j]=f2963[i][j];
		}
		pre2963[j]=cur2963[j]=-1;
		int fi=1,pi=1,ci=1;
		while(fi<=K2963 && (pre2963[pi]!=-1 || cur2963[ci]!=-1))//合并排序的思想找到前K大的值
		{
			if(pre2963[pi]>cur2963[ci]) f2963[i][fi]=pre2963[pi++];
			else f2963[i][fi]=cur2963[ci++];
			if(f2963[i][fi]!=f2963[i][fi-1]) ++fi;//判断fi个最大值和fi-1个最大值是否相同,用于去掉重复
		}
	}
}
int main()
{
	freopen("in.txt","r",stdin);
	int tempcost=0;
	long maxdata=0;
	cin>>T2963;
	while(T2963--)
	{
		memset(f2963,0,sizeof(f2963));
		memset(v2963,0,sizeof(v2963));
		memset(pre2963,0,sizeof(pre2963));
		memset(cur2963,0,sizeof(cur2963));
		cin>>N2963>>V2963>>K2963;
		for(int i=0;i<N2963;++i)
			cin>>v2963[i];
		for(int i=0;i<N2963;++i)
		{
			cin>>tempcost;
			ZeroOnePack2963(v2963[i],tempcost);
		}
		cout<<f2963[V2963][K2963]<<endl;
	}
	return 0;
}


你可能感兴趣的:(Algorithm,01背包)