題目大意
做法
定義有數量限制的叫大件,其餘是小件。
考慮最小的那個體積v1。
如果連v1都是大件,DP容易解決。
不然的話,考慮在模v1意義下進行,最終要湊出的S必定是S%v1。
問題在於,湊出S%v1不一定能湊出S。
實際上,如果能湊出x,x+v1也能湊出。
因此考慮求出每個模意義下能湊出的最小數便可以每次判定能否湊出。
不過還有大件限制困擾我們。我們設f[i,j]表示用了i個大件,湊出在模意義下結果為j的最小數是多少,這個DP方程有後效性。
如果按照用了幾個大件分層,處理一層後可以推到下一層(加一個大件)相當於賦個初值,接下來一層內轉移圖形成環的形狀(對於同一個小件),找到環中最小值,指向它的邊可以刪去,便可以破環為鏈,進行簡單遞推了。
#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=50+10,maxd=10000+10;ll f[maxd][maxn],g[maxd];bool bz[maxd];int a[maxn];int i,j,k,l,r,id,c,t,n,m;ll v;int main(){ freopen("bag.in","r",stdin);freopen("bag.out","w",stdout); scanf("%d%d",&n,&m); fo(i,1,n) scanf("%d",&a[i]); scanf("%d%d",&l,&c); sort(a+1,a+n+1); fo(i,1,n){ if (a[i]>=l) break; t=i; } fo(i,0,a[1]) fo(j,0,c) f[i][j]=1000000000000000005; fo(i,0,a[1]) g[i]=1000000000000000005; f[0][0]=0; g[0]=0; fo(j,0,c){ if (j){ fd(k,n,t+1){ fo(i,0,a[1]-1) f[i][j]=min(f[i][j],f[((i-a[k])%a[1]+a[1])%a[1]][j-1]+(ll)a[k]); } } fd(k,t,2){ fo(i,0,a[1]-1) bz[i]=0; fo(i,0,a[1]-1) if (!bz[i]){ r=i;id=i; while (!bz[r]){ bz[r]=1; if (f[r][j]<f[id][j]) id=r; r=(r+a[k])%a[1]; } r=id; while ((r+a[k])%a[1]!=id){ f[(r+a[k])%a[1]][j]=min(f[(r+a[k])%a[1]][j],f[r][j]+(ll)a[k]); r=(r+a[k])%a[1]; } } } fo(i,0,a[1]-1) g[i]=min(g[i],f[i][j]); } while (m--){ scanf("%lld",&v); if (g[v%a[1]]<=v) printf("Yes\n");else printf("No\n"); }}