码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ】1082: [SCOI2005]栅栏(二分+dfs)

时间:2014-12-13 10:46:25      阅读:244      评论:0      收藏:0      [点我收藏+]

标签:des   blog   http   io   ar   os   使用   sp   for   

http://www.lydsy.com/JudgeOnline/problem.php?id=1082

最近被这种神题虐cry。。。这还竟然是usaco的题QAQ我竟然如此弱。。。。(我是不是写过这题?反正好像有点印象的样子。。好像又不是。。)

一开始写了个背包。。。贪心的找。。。。。。。。。。。。。。。。然后造了几个数据,,wa了。。

QAQ

膜拜题解。orz

首先我们得到的k个木板一定是在n个中最小的k个。。。(这个太显然了QAQ

我们考虑将m个提供的木材,依次从最小的放(显然先用完最短的。。。)

所以将k个木板和m个提供的木材排序后再做。。。。。

二分k,判定是否能得到。。。

那么就是暴力枚举前k个的放法,可是。。2^1000。。。。tle成翔。。。。。。。。

那么考虑剪枝orz

首先如果当一个提供的木材小于了最小的所需木板,那么我们用个rest累加,当rest+sum[k]>tot时,剪掉(sum[k]表示前k个所需木板长度,tot表示提供木板的总量(其实这个剪枝还不够呢。。。因为tot是静态的,并没有变动。。。反正不改也能a))

然后继续剪枝,当k木板等于k-1木板的长度,那么我们不需要从1枚举木材了,因为此时k木板从哪个枚举说明前面的都不够了。。所以直接从k木板当前的木材枚举k-1。。

然后能水过了。。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define error(x) (!(x)?puts("error"):0)
#define rdm(x, i) for(int i=ihead[x]; i; i=e[i].next)
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }

const int N=2005;
int v[N], n, m, w[N], sum[N], c[N], tot, rest;

bool dfs(int dep, int last) {
	if(!dep) return true;
	if(rest+sum[dep]>tot) return false;
	for1(i, last, n) {
		if(c[i]>=w[dep]) {
			c[i]-=w[dep];
			if(c[i]<w[1]) rest+=c[i];
			if(w[dep]==w[dep-1]) { if(dfs(dep-1, i)) return true; }
			else if(dfs(dep-1, 1)) return true;
			if(c[i]<w[1]) rest-=c[i];
			c[i]+=w[dep];
		}
	}
	return false;
}
bool check(int d) {
	memcpy(c, v, sizeof(int)*(n+1));
	rest=0;
	return dfs(d, 1);
}
int main() {
	read(n);
	for1(i, 1, n) read(v[i]), tot+=v[i];
	sort(v+1, v+1+n);
	read(m);
	for1(i, 1, m) read(w[i]);
	sort(w+1, w+1+m);
	for1(i, 1, m) sum[i]=sum[i-1]+w[i];
	while(sum[m]>tot) --m;
	int l=0, r=m, mid;
	while(l<=r) {
		mid=(l+r)>>1;
		if(check(mid)) l=mid+1;
		else r=mid-1;
	}
	print(l-1);
	return 0;
}

  

 


 

 

Description

农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为10的木板可以切成长度为8和2的两个木板。你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰最多能够得到多少他所需要的木板。

Input

第一行为整数m(m<= 50)表示木材店老板可以提供多少块木材给约翰。紧跟着m行为老板提供的每一块木板的长度。接下来一行(即第m+2行)为整数n(n <= 1000),表示约翰需要多少木材。接下来n行表示他所需要的每一块木板的长度。木材的规格小于32767。(对于店老板提供的和约翰需要的每块木板,你只能使用一次)。

Output

只有一行,为约翰最多能够得到的符合条件的木板的个数。

Sample Input

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

Sample Output

7

HINT

 

25切出 21 30切出 20 40切出 19、18 50切出 15、16、17

 

Source

 

 

【BZOJ】1082: [SCOI2005]栅栏(二分+dfs)

标签:des   blog   http   io   ar   os   使用   sp   for   

原文地址:http://www.cnblogs.com/iwtwiioi/p/4160961.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!