一个整数数组,长度为n,将其分为m份,使得各分的和相等

本文介绍了一种使用多重背包算法来解决将整数数组等分成若干组的问题。通过遍历数组找到最大值和总和,确定可能的最大等分数,再利用多重背包算法求解每份的组合。
//题意:
//一个整数数组,长度为n,将其分为m份,使得各分的和相等
//比如:{3,4,2,3,6}可以分成{3,2,4,3,6} m=1;或者 {3,6},{2,4,3} m=2;
//{3,3},{2,4}{6} m=3,所以m最大值为3
//思路:使用多重背包
//首先遍历一次数组arr[n],把数组中最大值maxN和总和sum记录下来
//那么可能的等分为m = sum / maxN
//在这m份中,假设为p份,每份都为sum/m

//然后利用多重背包求每份sum/m;

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

#define MSIZ 1000
#define NSIZ 10000

int dp[NSIZ];
int v, n;
int path[NSIZ][MSIZ];
int num[NSIZ];
typedef struct Node_
{
	int data;
	int index;
}Node;
Node arr[MSIZ];
vector<vector<int>> vec(MSIZ);

int cmp(int a, int b)
{
	return a > b;
}

void zeroPacket(int cost, int value, int index,  int id)
{
	int i = 0;
	for (i = v; i >= cost; i--)
	{
		if (dp[i] < dp[i - cost] + value)
		{
			dp[i] = max(dp[i], dp[i - cost] + value);
			memcpy(path[i], path[i - cost], sizeof(path[i - cost]));
			path[i][index] = id;
		}

	}
}

void completePacket(int cost, int value, int index, int id)
{
	int i = 0;
	for (i = cost; i <= v; ++i)
	{
		if (dp[i] < dp[i - cost] + value)
		{
			dp[i] = max(dp[i], dp[i - cost] + value);
			memcpy(path[i], path[i - cost], sizeof(path[i - cost]));
			path[i][index] = id;
		}
	}
}

void multiPacket(int cost, int value, int m,int index,  int id)
{
	if (m * cost >= v)
	{
		completePacket(cost, value,index, id);
		return;
	}

	int k = 1;
	while(k <= m)
	{
		zeroPacket(k * cost, k * value,index, id);
		m -= k;
		k *= 2;
	}

	if (m)
	{
		zeroPacket(m * cost, m * value, index, id);
	}
}

int main()
{
	int t, i, j, p, q, k;
	int sum = 0;
	int maxN = INT_MIN;
	path[0][0] = true;
	j = 0;
	int row;

	while (scanf("%d", &t) != EOF)
	{
		memset(num, 0, sizeof(num));
		memset(dp, 0, sizeof(dp));
		memset(path, -1, sizeof(path));
		sum = 0;
		for (i = 0;i < t; ++i)
		{
			scanf("%d", &arr[i].data);
			arr[i].index = i;
			sum += arr[i].data;

			if (maxN < arr[i].data)
			{
				maxN = arr[i].data;
			}
		}

		int m = sum / maxN;
		for (p = m; p >= 2; --p)
		{
			v = sum / p;
			q = 0;
			for (i = 0;i < p; ++i)
			{
				vec[i].clear();
			}
			j = 0;
			do
			{
				memset(dp, 0, sizeof(dp));
				for (i = 0;i < t; ++i)
				{

					if (arr[i].index != -1)
					{
						multiPacket(arr[i].data, arr[i].data, 1, arr[i].index, q);
					}
				}

				if (dp[v] != v)
				{
					break;
				}

				for (i = 0, k = 0;i < t; ++i)
				{
					if (path[v][arr[i].index] == q)
					{
						vec[j].push_back(arr[i].data); 
						arr[i].index = -1;
					}
				}
				++j;
				row = j;


			}while(++q < p);

			if (q == p)
			{
				for (i = 0;i < row;++i)
				{
					for (j = 0; j < vec[i].size(); ++j)
					{
						printf("%d ", vec[i][j]);
					}
					printf("\n");
				}
				break;
			}

		}

		if (q != p)
		{
			for (i = 0;i < t; ++i)
			{
				printf("%d ", arr[i].data);
			}

			printf("\n");
		}
		else
		{
			printf("\n");
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值