1. 程式人生 > >【BZOJ 1082】[SCOI2005]柵欄 二分+dfs

【BZOJ 1082】[SCOI2005]柵欄 二分+dfs

log col con can 很大的 明顯 scan cstring 時間

對於最優解我們發現所有的最優解都可以是前多少多少個,那麽我們就二分這個前多少多少個,然後用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