usaco 4.1.2

Fence Rails
Burch, Kolstad, and Schrijvers

Farmer John is trying to erect a fence around part of his field. He has decided on the shape of the fence and has even already installed the posts, but he's having a problem with the rails. The local lumber store has dropped off boards of varying lengths; Farmer John must create as many of the rails he needs from the supplied boards.

Of course, Farmer John can cut the boards, so a 9 foot board can be cut into a 5 foot rail and a 4 foot rail (or three 3 foot rails, etc.). Farmer John has an `ideal saw', so ignore the `kerf' (distance lost during sawing); presume that perfect cuts can be made.

The lengths required for the rails might or might not include duplicates (e.g., a three foot rail and also another three foot rail might both be required). There is no need to manufacture more rails (or more of any kind of rail) than called for the list of required rails.

PROGRAM NAME: fence8

INPUT FORMAT

Line 1:N (1 <= N <= 50), the number of boards
Line 2..N+1:N lines, each containing a single integer that represents the length of one supplied board
Line N+2:R (1 <= R <= 1023), the number of rails
Line N+3..N+R+1:R lines, each containing a single integer (1 <= ri <= 128) that represents the length of a single required fence rail

SAMPLE INPUT (file fence8.in)

4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30

OUTPUT FORMAT

A single integer on a line that is the total number of fence rails that can be cut from the supplied boards. Of course, it might not be possible to cut all the possible rails from the given boards.

SAMPLE OUTPUT (file fence8.out)

7
 
     把题目换一种熟悉的说法就是,将n件物品放入m个背包里,最多放入多少个。首先对数据进行必要的处理。对物品和背包排序。物品体积大于最大背包容量的去掉,背包容量小于最小物品体积的去掉。剩下的物品和背包中,如果前i件较小物品体积和小于等于背包总体积,前i+1件较小物品的体积和大于背包总体积,则留下前i件物品,其他的不要。
    对于这道题目,广搜空间消耗太大,深搜又不能在得到正确结果时及时退出搜索,效率不高。这道题的搜索方法我第一次用,迭代加深搜索。
    说说搜索的策略,很显然如果最多放入k件物品,那么前k件较小的物品一定能放入背包。于是每次搜索解决的问题就是,前k件较小物品能否放入背包。在一次搜索中,如果下一件将要放入的物品体积与当前放入背包物品体积相等,那么下一件物品所放入的背包体积一定不小于当前物品所放入背包的体积,这样可以避免重复的搜索。
    搜索前k个较小物品是否能放入背包有下面的剪枝的方法,每次放入物品之后,更新浪费容量的大小。所谓浪费的容量就是背包剩余的容量小于最小物品的体积。检测一下,如果当前浪费容量的总和大于背包容量总和减去前k个物品体积,那么现在这宗状态肯定不能把k个物品放入背包。将当前物品取出,放入下一个背包继续搜索。
ps:在拜读各大牛的报告后,有了几个新的优化。1.二分法,夹逼求解。2.搜索顺序,物品从大到小,背包从小到大。尚不了解第二点的理论依据。
/*
ID: fwj_ona1
LANG: C++
TASK: fence8
*/

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
using namespace std;

int n,r,board[50],rail[1023],sumboard,sumrail[1023],rest,mid;

int cmp(const void *a, const void *b);
int cmp2(const void *a, const void *b);
bool dfs(int ra,int bo);
int main ()
{
	freopen ("fence8.in","r",stdin);
    freopen ("fence8.out","w",stdout);
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",&board[i]);
	scanf("%d",&r);
	for(int i=0;i<r;i++)
		scanf("%d",&rail[i]);

	qsort(board,n,sizeof(board[0]),cmp2);
	qsort(rail,r,sizeof(rail[0]),cmp);

	for(int i=r-1;i>=0;i--)
		if(rail[i]<=board[0])
			break;
		else
			r--;
	
	if(r==0)
	{
		printf("0\n");
		return 0;
	}

	for(int i=0;i<n;i++)
		if(board[i]<rail[0])
		{
			n=i;
			break;
		}

	for(int i=0;i<n;i++)
		sumboard+=board[i];
	sumrail[0]=rail[0];
	for(int i=1;i<r;i++)
	{
		sumrail[i]=sumrail[i-1]+rail[i];
		if(sumrail[i]>sumboard)
		{
			r=i;
			break;
		}
	}
	int left,right,ans;
	left=0,right=r-1,mid=(left+right)/2;
	while(left<=right)
		if(dfs(mid,n-1))
		{
			ans=mid+1;
			left=mid+1;
			mid=(left+right)/2;
		}
		else
		{
			right=mid-1;
			mid=(left+right)/2;
		}
	printf("%d\n",ans);
	return 0;
}

int cmp(const void *a, const void *b)
{
	return(*(int *)a-*(int *)b);
}

int cmp2(const void *a, const void *b)
{
	return(*(int *)b-*(int *)a);
}

bool dfs(int ra,int bo)
{
	if(ra<0)
		return true;
	for(int i=bo;i>=0;i--)
		if(board[i]>=rail[ra])
		{
			board[i]-=rail[ra];
			if(board[i]<rail[0])
				rest+=board[i];
			if(rest>sumboard-sumrail[mid])
			{
				if(board[i]<rail[0])
					rest-=board[i];
				board[i]+=rail[ra];
				continue;
			}
			if(rail[ra]==rail[ra-1])
			{
				if(dfs(ra-1,i))
				{
					if(board[i]<rail[0])
						rest-=board[i];
					board[i]+=rail[ra];
					return true;
				}
			}
			else
				if(dfs(ra-1,n-1))
				{
					if(board[i]<rail[0])
						rest-=board[i];
					board[i]+=rail[ra];
					return true;
				}
			if(board[i]<rail[0])
				rest-=board[i];
			board[i]+=rail[ra];
		}
	return false;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值