BZOJ 1082[SCOI2005]栅栏 二分+dfs

Posted TS_Hugh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 1082[SCOI2005]栅栏 二分+dfs相关的知识,希望对你有一定的参考价值。

对于最优解我们发现所有的最优解都可以是前多少多少个,那么我们就二分这个前多少多少个,然后用dfs去判解,我们发现在dfs的过程中如果不剪枝几乎必T,所以我们就需要一些有效的剪枝 I. 我们在枚举过程中每个数选什么是有前后顺序的,然而对于一些相同的数他们并没有顺序我们可以记录上个数的选择点,如果两数相同,那么就从上个数的选择点开始那么时间复杂度就从次方级别降到了组合数级别,是飞跃式的 II. 然后我们发现先对于枚举顺序与选择顺序的选择是玄学的,我们可以视为他们都没影响那么我们就可利用这个了,如果我们倒着走枚举顺序那么我们可以把剩下的长度小于最小木板的木材删去,然后如果全部的减去删掉的小于需要的就退出。

对于搜索,我们有许多明显的减枝,那些都必须要减掉,然而有一些看似不起眼的小减枝在有的数据里往往会发挥很大的作用因此我们不能放弃任何一个减枝的机会。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define Max(a,b) a>b?a:b;
const int M=55;
const int N=1055;
const int K=40000;
int g[M],f[N],n,m,ans,s[N],S;
bool die[M];
inline void read(){
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&g[i]);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&f[i]);
    std::sort(g+1,g+(m+1));
    std::sort(f+1,f+(n+1));
    while(f[n]>g[m]&&n>0)n--;
    int z=1,tot;
    while(f[1]>g[z]&&z<=m)z++;
    tot=m-z+1;
    for(int i=1,j=z;i<=tot;i++,j++)
        g[i]=g[j],S+=g[i];
    for(int i=1;i<=n;i++)s[i]=s[i-1]+f[i];
    m=tot;
}
bool dfs(int pos,int mid,int last,int key,int waste){
    if(pos==0)return true;
    if(waste+s[mid]>S)return false;
    for(int i=(f[pos]==key?last:1);i<=m;i++)
        if(g[i]>=f[pos]){
            g[i]-=f[pos];
            if(g[i]<f[1])waste+=g[i];
            if(dfs(pos-1,mid,i,f[pos],waste)){
                g[i]+=f[pos];
                return true;
            }
            if(g[i]<f[1])waste-=g[i];
            g[i]+=f[pos];
        }
    return false;
}
int main(){
    read();
    int l=0,r=n,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(dfs(mid,mid,0,0,0))
            ans=mid,l=mid+1;
        else
            r=mid-1;
    }
    printf("%d",ans);
    return 0;
}

 

以上是关于BZOJ 1082[SCOI2005]栅栏 二分+dfs的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1082 SCOI2005—栅栏

BZOJ-1082-[SCOI2005]栅栏(二分+dfs判定)

BZOJ 1082[SCOI2005]栅栏 二分+dfs

BZOJ1082: [SCOI2005]栅栏

BZOJ1082: [SCOI2005]栅栏 题解

BZOJ1082: [SCOI2005]栅栏