【NOIP2018 信心賽】滋潤
阿新 • • 發佈:2018-12-19
【問題描述】
餓了麼?萬科憋?香不香? 最喜歡去不遠處的van♂ke吃 最喜歡的脾腎嗑了。 勤儉持家,他總是打包剩
菜。 飯桌上可以看成有 個剩菜, 個打包盒,其中每個剩菜都有體積 ,每個打包盒都有容量 。為了環保
想知道最少要用幾個打包盒裝下所有的剩菜?注意, 認為剩菜不可分割,但是他並不介意把兩份或以上的菜裝
在一個打包盒裡。若無論如何都裝不下請輸出-1
【輸入格式】
輸入檔案為 輸入資料的第一行為兩個正整數 和 ,意義如題所示 接下來一行 個非負整數,第i個
表示 ,意義如題所示 接下來一行 個非負整數,第i個表示 ,意義如題所示
【輸出格式】
輸出檔案為 輸出檔案包含一行,表示最少的打包盒數量
【輸入輸出樣例】
見下發檔案 和
【樣例解釋】
用11的包裝下3,用18的包裝下10、4和2,共2個包 當然也存在別的只用2個包的裝法
思路
考慮狀壓dp。設f[x]表示選取物品狀態為x的最小揹包數量,g[x]為選取物品狀態為x,揹包數量為f[x]時最後一個包剩餘的空間,列舉x中1的位然後轉移就行了。考慮這樣做的正確性,我們一定枚舉出了所有可能的放置順序。這樣有80分
可以發現複雜度瓶頸在於列舉x中不為1的位,這個用樹狀陣列時呼叫的lowbit就可以優化到n2 。可以得到100分的高分
程式碼
#include <stdio.h> #include <string.h> #include <algorithm> #include <math.h> #define rep(i,st,ed) for (register int i=st;i<=ed;++i) #define drp(i,st,ed) for (register int i=st;i>=ed;--i) #define fill(x,t) memset(x,t,sizeof(x)) #define lowbit(x) ((x)&(-(x))) const int INF=10777216; const int N=16778216; int a[N],b[505],f[N],g[N],n,m; int read() { int x=0,v=1; char ch=getchar(); for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar()); for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar()); return x*v; } bool cmp(int x,int y) { return x>y; } int main(void) { freopen("wilgotne.in","r",stdin); freopen("wilgotne.out","w",stdout); n=read(),m=read(); int lim=(1<<n)-1; rep(i,1,n) a[i]=read(); drp(i,n,1) a[1<<(i-1)]=a[i]; rep(i,1,m) b[i]=read(); std:: sort(b+1,b+m+1,cmp); rep(i,1,lim) { f[i]=INF; for (int x=i;x;x-=lowbit(x)) { int tmp=lowbit(x); if (g[i-tmp]>=a[tmp]&&(f[i-tmp]<f[i]||(f[i-tmp]==f[i]&&g[i-tmp]-a[tmp]>g[i]))) { f[i]=f[i-tmp]; g[i]=g[i-tmp]-a[tmp]; } else if (b[f[i-tmp]+1]>=a[tmp]&&(f[i-tmp]+1<f[i]||(f[i-tmp]+1==f[i]&&b[f[i-tmp]+1]-a[tmp]>g[i]))) { f[i]=f[i-tmp]+1; g[i]=b[f[i]]-a[tmp]; } } } if (f[lim]>m) puts("-1"); else printf("%d\n", f[lim]); return 0; }