【 OJ 】 HDOJ1059 多重背包求大理石分配问题 [ 51 ]

36 篇文章 0 订阅
5 篇文章 0 订阅

Time Limit Exceeded

直接DFS暴搜.....死的很安详,然后写了母函数的解法,死的也很安详超时(:逃 江湖传言 sum取模60可AC多重背解未被AC

关于多重背包的解法不想说啥,这里就是将承重m换成了价值v来理解筛选,用价值V来刷选最大可以筛选出Vmax及dp[V]=V,如果不等说明这个价值不可以被组合选举出来,其中多重背包二进制转化01背包也有很详细的讲解...

如果不懂可以看我的自学笔记关于背包问题的,我从不会到会的所有理解,非常详细......

(:机票  https://blog.csdn.net/QingCoffe/article/details/85239055 如果你不会01背包问题,我想你看完的收益会很大

01/多重/完全/背包问题等 (:逃 

多重背包解法:(没被AC搞不懂为啥,用可AC代码自检,发现代码输出结果没问题.....不知道哪里出了问题懒得搞了)

# include<iostream>
# include<algorithm>
#pragma warning (disable:4996)
using namespace std;
int n[6];
int val[20010],dp[60020];
int index;
void OUT(bool b) {
	static int index = 0;
	printf("Collection #%d\n", ++index);
	if (b) {
		printf("Can be divided.\n\n");
	}
	else {
		printf("Can't be divided.\n\n");
	}
}
int INput(void) {
	int total_ = 0;
	for (int i = 0; i < 6; i++) {
		scanf("%d", &n[i]);
		total_ += (i + 1)*n[i];
		for (int j = 1; j <= n[i]; j <<= 1) {
			val[index++] = (i + 1)*j;
			n[i] -= j;
		}
		if (n[i]) {
			val[index++] = (i + 1)*n[i];
		}
	}
	return total_;
}
int main(void) {
	int total;
	int index = 0;
	while (1) {
		index = 0;
		total = INput();//录入下一次信息
		if (!total)break;//全0
		if (total & 1)OUT(false);//总价值为奇数不可分割
		else {//尝试分割
			total >>= 1;
			int i, j;
			memset(dp, 0, sizeof(dp));//初始化dp数组
			for (i = 0; i < index; i++) {//物品从第一个一次排列
				for (j = total; j >= val[i]; j--) {//01倒序
						dp[j] = max(dp[j], dp[j - val[i]] + val[i]);
				}
			}
			if (dp[total] == total)OUT(true);
			else OUT(false);
		}
	}
	system("pause");
	return 0;
}

母函数:

# include<iostream>
# include<algorithm>
# define Inf 120000
using namespace std;
int n[6];
int a[Inf], b[Inf];
void OUT(bool b) {
	static int index = 0;
	if (index)cout << endl;
	cout << "Collection #" << ++index << ":" << endl;
	if (b) {
		cout << "Can be divided." << endl;
	}
	else {
		cout << "Can't be divided." << endl;
	}
}
int INput(void) {
	int total_ = 0;
	for (int i = 0; i < 6; i++) {
		cin >> n[i];//录入弹珠信息
		total_ += (i + 1)*n[i];
	}
	return total_;
}
int main(void) {
	int total = INput();//拿到第一个输入的价值
	while (!((*max_element(n, n + 6)) == 0)) {//全0不做
		if (total & 1)OUT(false);//总价值为奇数不可分割
		else {//尝试分割
			total /= 2;
			memset(a, 0, sizeof(a));
			memset(b, 0, sizeof(b));
			int i, j, k;
			for (i = 0; i<=total&&i <= n[0]; i++) a[i] = 1;
			for ( i = 1; i < 6; i++) {//共6个表达式,从第2个表达式到第6个表达式
				if (!n[i])continue;//个数为0,第i个表达式不存在
				for ( j = 0; j <= total; j++) {//计算出的表达式指数从0循环到total
					for (k = 0; a[j] && k <= total&&k <= (n[i] * (i + 1)); k += i+1) {//for (k = 0;k <= coin[i] * i; k += i)
						if (j + k > total)break;
						b[j + k] += a[j];
					}
				}
				for (k = 0; k <= total; k++) {
					a[k] = b[k]; b[k] = 0;
				}//刷新数组a的值,b数组清零
			}
			if (a[total])OUT(true);
			else OUT(false);
		}
		total = INput();//录入下一次信息
	}
	system("pause");
	return 0;
}

DFS暴搜:

# include<iostream>
# include<algorithm>
//# include<Windows.h>
using namespace std;
int n[6] = { 0 };
int ans, total, flag;
int Visit[6];
void OUT(bool b) {
	static int index = 0;
	if (index)cout << endl;
	cout << "Collection #" << ++index << ":" << endl;
	if (b) {
		cout << "Can be divided." << endl;
	}
	else {
		cout << "Can't be divided." << endl;
	}
}
int INput(void) {
	int total_ = 0;
	for (int i = 0; i < 6; i++) {
		cin >> n[i];//录入弹珠信息
		Visit[i] = n[i];
		total_ += (i+1)*n[i];
	}
	return total_;
}
void Fen(int index) {//index 层数一共6层,i第几个物品 n[i]为该层有的物品数
	for (int i = index; i < 6 && !flag; i++) {
		if (!index) {//0 层 ans =0;初始化访问表
			ans = 0;
			for (int j = 0; j < 6; j++) {
				Visit[j] = n[j];
			}	//初始化访问表
		}
		if (!Visit[i])continue;
		for (int k = 0; k <= n[i]; k++) {//首先不拿---直到拿n[i]个
			Visit[i] -= k;//先拿K个
			ans += (i + 1)*k;	//得到K个总价值
			if (ans < (total / 2)) {//价值小于目标数
				Fen(i + 1);//递归到下一层
				ans -= (i + 1)*k; //拿回的东西放回去
				Visit[i] += k;
			}
			else if (ans == total / 2) {
				flag = true; return;//找到并返回
			}
			else {//价格已经超出了 剪枝
				ans -= (i + 1)*k; //东西放回去 结束
				Visit[i] += k;
				return;
			}
		}

	}
}
int main()
{
	total = INput();//拿到第一个输入的价值
	while (!((*max_element(n, n + 6)) == 0)) {//全0不做
		if (total & 1)OUT(false);//总价值为奇数不可分割
		else {//尝试分割
			flag = false;//未找到
			Fen(0);//从0开始找
			if (flag)OUT(true);
			else OUT(false);
		}
		total = INput();//录入下一次信息
	}
	system("pause");
	return 0;
}

因为超时所以不知道准确答案,因此我写了个检测......用的别人正确的AC码做参考:随机生成了10W个数据显示是对的

# include<iostream>
# include<algorithm>
#include <cstdio>
#include <cstring>
//# include<Windows.h>
using namespace std;
int n[6] = { 0 };
int a[6];
int ans, total, flag;
int Visit[6];
const int SIZE = 120000 + 16;
int dp[SIZE];
void Fen(int index) {//index 层数一共6层,i第几个物品
	for (int i = index; i < 6 && !flag; i++) {
		if (!index) {//0 层 ans =0;初始化访问表
			ans = 0;
			for (int j = 0; j < 6; j++) {
				Visit[j] = n[j];
			}	//初始化访问表
		}
		if (!Visit[i])continue;
		for (int k = 0; k <= n[i]; k++) {//首先不拿---直到拿n[i]个
			Visit[i] -= k;//先拿K个
			ans += (i + 1)*k;	//得到K个总价值
			if (ans < (total / 2)) {//价值小于目标数
				Fen(i + 1);//递归到下一层
				ans -= (i + 1)*k; //拿回的东西放回去
				Visit[i] += k;
			}
			else if (ans == total / 2) {
				flag = true; return;//找到并返回
			}
			else {//价格已经超出了 剪枝
				ans -= (i + 1)*k; //东西放回去 结束
				Visit[i] += k;
				return;
			}
		}

	}
}
void RAND(void) {
	total = 0;
	for (int i = 0; i < 6; i++) {
		n[i] = rand() % 5 + 1;
		a[i] = n[i];
		Visit[i] = n[i];
		total += (i + 1)*n[i];
	}
}
bool My(void) {
	
	if (total & 1)return false;//总价值为奇数不可分割
	else {//尝试分割
		flag = false;//未找到
		Fen(0);//从0开始找
		if (flag)return true;
		else return false;
	}
}
bool check()
{
	for (int i = 0; i<6; i++)
		if (a[i] != 0)
			return true;
	return false;
}
bool YO(void) {
	int sum;
	int t = 0;
	while (true)
	{
		sum = 0;
		for (int i = 0; i<6; i++)
		{
			sum += (i + 1)*a[i];
		}
		if (!check())
			break;
		if (sum % 2 == 1)
		{
			return false;
		}
		else
		{
			memset(dp, -1, sizeof(dp));
			int k = sum / 2;
			dp[0] = 0;
			for (int i = 0; i<6; i++)
			{
				for (int j = 0; j <= k; j++)
				{
					if (dp[j] >= 0)
					{
						dp[j] = a[i];
					}
					else if (j<(i + 1) || dp[j - (i + 1)] <= 0)
					{
						dp[j] = -1;
					}
					else
					{
						dp[j] = dp[j - (i + 1)] - 1;
					}
				}
			}
			if (dp[k] >= 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
	}
}
int main()
{
	bool re1, re2;
	int index = 0;
	while((index+1)<10000){
		cout << ++index << endl;
		RAND();
		re1 = My();
		re2 = YO();
		if (re1 != re2) {
			cout << "出现不同结果:" << endl;
			cout << "--------------------" << endl;
			cout << "MY:" << re1 << endl;
			cout << "--------------------" << endl;
			cout << "--------------------" << endl;
			cout << "YO:" << re2 << endl;
			cout << "--------------------" << endl;
			cout << "--------------------" << endl;
			cout << "n 的情况: " << endl;
			for (int i = 0; i<6; i++)
			{
				cout <<i+1<<" : "<< a[i] << "   "<<n[i]<<endl;
			}
			cout << "--------------------" << endl;
			getchar();
		}
	}
	
	system("pause");
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值