首页 > 代码库 > USACO/fence8 迭代加深搜索+剪枝

USACO/fence8 迭代加深搜索+剪枝

题目链接

迭代加深搜索思想。

枚举答案K,考虑到能否切出K个木头,那么我们当然选最小的K个来切。

1、对于原材料,我们是首选最大的还是最小的?显然,首选大的能够更容易切出,也更容易得到答案。

2、对于目标木头,我们是优先得到最大的还是最小的?显然,由于K个木头我们都要得到,那么当然先把最大的(最难得到的)先得到,这种搜索策略更优。

3、假设总原材料为all,前K个木头总和为sum,那么all-sum就是这一次切割过程中能【浪费】的最大数目。对于一个切剩下的原材料,若它比最小的目标木头还要小,则它可视为【无用】的,无用的也就是浪费的,若浪费>all-sum,则直接返回false

4、对于一个目标木头B,若它的长度和上一个木头A的长度相同,那么我们切B所用的原材料(B‘)一定是从切A所用的原材料(A‘)位置开始找。(这其实就是一个剪掉重复计算的剪枝)

5、迭代可以使用二分,但其实枚举也慢不了多少。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m,a[55],b[1100],maxs=0,sums[1100];
int mid;
bool cmp(int x,int y)
{
    return x>y;
}
bool dfs(int ia,int ib,int sum,int all)
{
    if(ib==0) return true;
    if(sum>all) return false;
    for(int i=ia;i<=n;i++)
    {
        if(a[i]>=b[ib])
        {
            a[i]-=b[ib];
            int tmp=sum+(a[i]<b[1]?a[i]:0);
            int st=(b[ib]==b[ib-1]?i:1);
            if(dfs(st,ib-1,tmp,all))
            {
                a[i]+=b[ib];
                return true;
            }
            a[i]+=b[ib];
        }
    }
    return false;
}
int main()
{
    freopen("fence8.in","r",stdin);
    freopen("fence8.out","w",stdout);
    int all=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        all+=a[i];
        maxs=max(maxs,a[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&b[i]);
        if(b[i]>maxs) {i--;m--;}
    }
    sort(a+1,a+1+n,cmp);
    sort(b+1,b+1+m);
    for(int i=1;i<=m;i++)
        sums[i]=sums[i-1]+b[i];
    int l=0,r=m,ans;
    for(ans=0;ans<=m&&dfs(1,ans,0,all-sums[ans]);ans++) ;
    printf("%d\n",ans-1);
    return 0;
}