USACO 4.1.2 栅栏的木料

这个讲的超好....一定要看...然后看我代码就好懂啦...

http://blog.csdn.net/ta201314/article/details/41287567

各种优化确实非常好....搜索的一道好题...

挂代码:

/*
    Problem : USACO 4.1.2 栅栏的木料
    Author : Robert Yuan
    
    优化解释:
    0. 二分+贪心判断可行解 (根据自己设计的贪心算法尽量的得到一个比较靠近正确值的 ans,再后面的搜索的下界就会大大提高)
    1. 如果使用的木料与上一块的木料大小相同,那么上一块若没有选前 i块木板,这一块也不要选(1.若是因为前面的太小而不选,那么对于这个也太小  2.若是因为前面的情况已经搜过,那我选择前面的就成了一个已经搜索过的情况)
    2. 如果当前需要枚举的木板与上一块木板的大小相同,只选其中最后的那块,理由同上。
    3. 如果剩下的木板大小比第一块还小,那么就是属于余料,将所有余料和已经切好的木料减去,剩下的木板长度如果比未切好的 x个木料总长还要小,那么这种状态也是不可行的 
*/

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

inline int in(){
    int x=0;char ch=getchar();
    while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}

const int maxn=52;
const int maxm=1025;
const int INF=0x7f7f7f7f;

int n,m,S,ans;
int tmp[maxn];
int a[maxn],b[maxm],s[maxm];

bool cmp(const int &a,const int &b){
    return a>b;
}

bool dfs(int x,int last){
    if(!x) return true;
    if(S<s[x]) return false;/*优化 3*/
    S-=b[x];
    for(int i= x!=ans && b[x]==b[x+1] ? last:1/*优化 1*/;i<=n;i++){
        while(i<n && a[i]==a[i+1]) i++;/*优化 2*/
        if(a[i]>=b[x]){
            a[i]-=b[x];
            if(a[i]<b[1]) S-=a[i];
            if(dfs(x-1,i)){
                if(a[i]<b[1]) S+=a[i];
                a[i]+=b[x];    S+=b[x];
                return true;
            }
            if(a[i]<b[1]) S+=a[i];
            a[i]+=b[x];
        }
    }
    S+=b[x];
    return false;
}

bool check(int m){/*优化 0*/
    int Minn,Minx;
    memcpy(tmp,a,sizeof(a));
    for(int i=m;i;i--){
        Minn=INF,Minx=-1;
        for(int j=1;j<=n;j++)
            if(tmp[j]>=b[i] && Minn>tmp[j])
                Minn=tmp[j],Minx=j;
        if(Minx<0) return false;
        tmp[Minx]-=b[i];
    }
    return true;
}


int main(){    
    n=in();
    for(int i=1;i<=n;i++) a[i]=in(),S+=a[i];
    m=in();
    for(int i=1;i<=m;i++) b[i]=in();
    
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    
    for(int i=1;i<=m;i++)
        s[i]=s[i-1]+b[i];
    
    int l=0,r=m,mid;
    
    if(check(r)){
        printf("%d",m);return 0;
    }
    
    while(l+1<r){
        mid=(l+r)>>1;
        if(check(mid)) l=mid;
        else r=mid;
    }
    ans=r;
    while(dfs(ans,1) && ans<=m) ans++;
    printf("%d",ans-1);
    
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Robert-Yuan/p/4816643.html