完全背包问题

时间:2022-03-17 18:39:06

题目大意

完全背包问题

做法

定义有数量限制的叫大件,其余是小件。
考虑最小的那个体积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");
}
}