您的位置:首页 > 其它

bzoj1082 跨栏 二分&搜索

2015-12-06 12:40 351 查看
果然搜索的姿势很重要啊。首先二分答案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
,a
,b
;
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: