【USACO4.1.2】栅栏的木料 爆搜剪枝优化

Executing...
   Test 1: TEST OK [0.005 secs, 3392 KB]
   Test 2: TEST OK [0.005 secs, 3392 KB]
   Test 3: TEST OK [0.008 secs, 3392 KB]
   Test 4: TEST OK [0.097 secs, 3392 KB]
   Test 5: TEST OK [0.005 secs, 3392 KB]
   Test 6: TEST OK [0.062 secs, 3392 KB]
   Test 7: TEST OK [0.005 secs, 3392 KB]
   Test 8: TEST OK [0.011 secs, 3392 KB]
   Test 9: TEST OK [0.030 secs, 3392 KB]
   Test 10: TEST OK [0.005 secs, 3392 KB]
   Test 11: TEST OK [0.005 secs, 3392 KB]
   Test 12: TEST OK [0.008 secs, 3392 KB]

All tests OK.


二分答案! 我真的没想到,看了nocow的题解。


把need从小到大排序


1、假设前k个need可以得到,那么首先可以知道前k个need木板的总和,也就可以计算出需要浪费的木板总量tot_wast。

因为已经精确到要浪费的总量了,某个切割方案让浪费的总量超过tot_wast,显然是无解的


2、因为只有128种数字,却有1000多木板,所以各种木板是重复的大小,这就有一个搜索次序的情况了。 比如有2个need木板都是5,5.

在当前的give的木板上,我切后一块,不要前一块。 这样会造成大量的重复运算,所以需要用一些方法来处理掉这些多余的计算量。


3、尝试要哪些need的时候,要从大到小! 


	for (int i = max_need; i >=0 ; -- i)
	{
		if (vis[i])	continue;
		if (len < need[i])	continue; //不够切的,也直接continue
		vis[i] = 1;
		//cut_num[i] = k; // i是被k切的
		if (i && need[i] == need[i - 1] && !vis[i - 1])
		{
			vis[i] = 0;
			continue;
		}
		flag = dfs(k, len - need[i], get + 1, wast);
		if (flag)	return true;
		vis[i] = 0;
		IFUSD =true;
	}


for (int i = max_need; i >=0 ; -- i)

改为

for (int i = 0; i <= <span style="font-family: Arial, Helvetica, sans-serif;">max_need</span> ; ++ i)  

时间差距巨大无比! 后者跑了20秒都不出答案……前者就快多了.


原因? 数据的原因嘛?我不认为如此。

首先众所周知,索树在开始的树枝被剪的效果永远比在结尾被剪效果来的好~

试大的,会很大程度的造成【快速浪费】,根据前面的浪费条件的剪枝原理,快速剪掉很多,快速出解。


/*
TASK:fence8
LANG:C++
*/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;

int gived_num, need_num;
int gived[55], need[1050];
int sum[1050]={0};
int tot_give(0);

void init()
{
	scanf("%d", &gived_num);
	for (int i = 0; i != gived_num; ++ i)	scanf("%d", gived + i);
	for (int i = 0; i != gived_num; ++ i)	tot_give += gived[i];
	scanf("%d", &need_num);
	for (int i = 0; i != need_num; ++ i)	scanf("%d", need + i);
	sort(need, need + need_num);
	sum[0] = need[0];
	for (int i = 1; i != need_num; ++ i)	sum[i] = need[i] + sum[i - 1]; //合计所用的材料
}

int max_wast, max_need;
bool vis[1050] = {false};
int cut_num[1050];

bool dfs(int k, int len, int get, int wast) //当前切第k个木料,第k个木料还剩多少长度, 浪费的总量
{
	if (wast > max_wast)	return false; //过度浪费,不可能成功,失败
	if (k == gived_num)	return false;
	if (get == max_need)
	{
		//cout<<"成功"<<endl;
		return true;	
	
	}
	bool flag = false;
	bool IFUSD = false;
	for (int i = max_need; i >=0 ; -- i)
	{
		if (vis[i])	continue;
		if (len < need[i])	continue; //不够切的,也直接continue
		vis[i] = 1;
		//cut_num[i] = k; // i是被k切的
		if (i && need[i] == need[i - 1] && !vis[i - 1])
		{
			vis[i] = 0;
			continue;
		}
		flag = dfs(k, len - need[i], get + 1, wast);
		if (flag)	return true;
		vis[i] = 0;
		IFUSD =true;
	}
	//下一块木料,这个不用了
	if (IFUSD) return false; //这块能切割,但是不切割,肯定是不行的,false
	flag = dfs(k + 1, gived[k + 1], get, wast + len);
	if (flag)	return true;
	return false;
}

bool check(int k) //前k个need全部制作出来
{
	memset(vis, 0, sizeof(vis));
	memset(cut_num, 0, sizeof(cut_num));
	sum[k]; //合计使用的材料
	max_wast = tot_give - sum[k]; //正好要浪费这么多材料,如果浪费更多,显然是不对的
	//cout<<tot_give<<" "<<sum[20]<<endl;
	if (sum[k] > tot_give)	return false;
	max_need = k;
	return dfs(0, gived[0], 0, 0);
}

void doit()
{
	int max_gived=0;
	for (int i = 0; i != gived_num; ++ i)	if (gived[i] > max_gived)	max_gived = gived[i];
	if (max_gived < need[0])
	{
		printf("0\n");
		return;
	}

	int left = 0, right = need_num; // [);
	int mid;
	while (left + 1 < right) //序号[left, right)合法
	{
		mid = (left + right) / 2;
		if (check(mid))	left = mid;
		else right = mid;
	}
	cout << left + 1 << endl;
}

int main()
{
	freopen("fence8.in","r",stdin);
	freopen("fence8.out","w",stdout);
	init();
	doit();
	return 0;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值