bzoj1082 跨栏 二分&搜索

       果然搜索的姿势很重要啊。首先二分答案x,那么剪下来的一定是最小的x个,然后枚举每一个从哪块上切下来就行了。有以下优化:

       1.将两个数组分别排序。那么所需要的木板一定是从大到小枚举,同时枚举每一个所需要的木板时,按照提供的木板的大小从小到大枚举能不能切去,这样容易回溯。

       2.对于一块提供的木板,如果其长度小于需要的最小的木板,那么剩下来的部分一定浪费了。记一个变量waste表示浪费的部分,那么如果waste+所需要的木板总长度>提供的木板总长度,直接回溯。

       3.对于相邻的两块所需要的木板,如果长度相等,那么后一块直接从前一块上次放的那一块开始枚举,避免重复运算。

       4.类似于小木棒,如果某一块所需要的木板刚好和已有的一块目前剩下的相等,如果这样也得不到答案,直接回溯。实测效果不大,没有写。

       5.二分不一定要从0~m,可以缩小范围。实测效果好像不是很大,懒得删了。

大概就这些了,AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;

int n,m,p,tot,wst,sum[N],a[N],b[N];
bool dfs(int k,int last){
	if (!k) return 1; int i;
	if (wst+sum[p]>tot) return 0;
	for (i=last; i<=n; i++)
		if (a[i]>=b[k]){
			a[i]-=b[k];
			if (a[i]<b[1]) wst+=a[i];
			if (b[k]==b[k-1]){
				if (dfs(k-1,i)){
					if (a[i]<b[1]) wst-=a[i];
					a[i]+=b[k]; return 1;
				}
			} else if (dfs(k-1,1)){
				if (a[i]<b[1]) wst-=a[i];
				a[i]+=b[k]; return 1;
			}
			if (a[i]<b[1]) wst-=a[i];
			a[i]+=b[k];
		}
	return 0;
}
bool ok(int x){
	p=x; wst=0; return dfs(p,1);
}
int main(){
	scanf("%d",&n); int i;
	for (i=1; i<=n; i++){
		scanf("%d",&a[i]); tot+=a[i];
	}
	scanf("%d",&m);
	int l=0,r=m;
	for (i=1; i<=m; i++) scanf("%d",&b[i]);
	sort(a+1,a+n+1); sort(b+1,b+m+1);
	for (i=1; i<=m; i++){
		sum[i]=sum[i-1]+b[i];
		if (sum[i]>tot) r=min(r,i-1); if (a[i]>b[i]) l++;
	}
	while (l+1<r){
		int mid=(l+r)>>1; 
		if (ok(mid)) l=mid; else r=mid-1;
	}
	if (l==r) printf("%d\n",l); else
		if (ok(r)) printf("%d\n",r); else printf("%d\n",l);
	return 0;
}

by lych

2015.12.6

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值