poj 1014 Dividing 二进制拆分,单调队列

题目大意:给你6种大理石,每种大理石i的个数为num[i],价值为i,问你有没有策略能时它分成两份,两份总价值相等。

解题思路:0/1多重背包问题。

dp[i][j] = max{dp[i - 1][j - k * i] + k * i}  这里的i为第i种大理石的价值, 0<=k<= num[i], dp[i][j] 表示选择前i种大理石,总价值不超过j时的最大价值总和。最后验证dp[n][mid] 是否等于mid , mid = sum(i * num[i]) / 2;

多重背包,根据拆分思想,把每种大理石num[i]拆分成2进制表示。然后转换成为0/1背包问题。

#include 
#include 
#include 
#include 

using namespace std;

int const maxn = 300010;
int num[7], value[maxn], tot;
int dp[maxn];

void split(int n, int v);

int main()
{
	
	int n = 1;
	while(true)
	{
		memset(dp, 0, sizeof(dp));
		int val = 0;
		for(int i = 1; i <= 6; i++)
		{
			scanf("%d", &num[i]);
			val += i * num[i];
		}
		if(val == 0)
			break;
			
		printf("Collection #%d:\n", n++);
			
		if((val & 1) != 0)
		{
			printf("Can't be divided.\n\n");
            continue;
		}
		tot = 0;
		for(int i = 1; i <= 6; i++)
			split(num[i], i);
			
		int mid = val >> 1;
		
		for(int i = 0; i < tot; i++)
		{
			for(int j = mid; j >= value[i]; j--)
				dp[j] = max(dp[j], dp[j - value[i]] + value[i]);
		}
		
		
		if(dp[mid] == mid)
			printf("Can be divided.\n\n");
		else
			printf("Can't be divided.\n\n");
	}
	
	return 0;
}

void split(int n, int v)
{
	int index = 0, x, tmp = 0;
	while(n > 0)
	{
		x = 1 << index;
		if(tmp + x > n)
			break;
		tmp += x;
		value[tot++] = x * v;
		index++;
	}
	x = n - tmp;
	if(x > 0)
		value[tot++] = x * v;
}
另一种方法为单调队列的方法,复杂度为O(VN)

具体的解释方法见这个链接,这里有详细解释,不做阐述

http://blog.csdn.net/flyinghearts/article/details/5898183

#include 
#include 
#include 
#include 

using namespace std;

struct node
{
	int index, value;
};

int const maxn = 300010;
int num[7], value[maxn], index[maxn], tot;
node que[maxn];
int dp[maxn];

void split(int n, int v);

int main()
{
	
	int n = 1;
	while(true)
	{
		memset(dp, 0, sizeof(dp));
		int val = 0;
		for(int i = 1; i <= 6; i++)
		{
			scanf("%d", &num[i]);
			val += i * num[i];
		}
		if(val == 0)
			break;
			
		printf("Collection #%d:\n", n++);
			
		if((val & 1) != 0)
		{
			printf("Can't be divided.\n\n");
            continue;
		}
		int mid = val >> 1;
		for(int i = 1; i <= 6; i++)
		{
			int t = i * num[i];
			if(num[i] == 1)
			{
				for(int j = mid; j >= i; j--)
					dp[j] = max(dp[j], dp[j - i] + i);
			}
			else if(t >= mid)
			{
				for(int j = i; j <= mid; j++)
					dp[j] = max(dp[j], dp[j - i] + i);
			}
			else
			{
				for(int j = 0; j < i; j++)
				{
					int head = 0, tail = -1;
					for(int k = j, nu = 0; k <= mid; k += i, nu++)
					{
						if(head <= tail)
						{
							if(k - que[head].index > t)
								head++;
						}
						int tt = dp[k] - nu * i;
						while(head <= tail && que[tail].value < tt)
							tail--;
						tail++;
						que[tail].index = k;
						que[tail].value = tt;
						dp[k] = que[head].value + nu * i;
					}
				}
			}
		}
		if(dp[mid] == mid)
			printf("Can be divided.\n\n");
		else
			printf("Can't be divided.\n\n");
	}
	
	return 0;
}



你可能感兴趣的:(ACM,动态规划,背包问题)