【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; }
苟利国家生死以, 岂因祸福避趋之。