[jzoj4916]完全背包问题——DP 大佬们的博客 Some Links

题目大意:

有n种物品,物品的体积分别为V1, V2, · · · , Vn,且每种物品的数量都可以看做是无 限多的。 现在有m次询问,每次询问给定一个容量为Wi的背包,请你回答是否存在一种物品选择方案,使得背包恰好能被完全装满(仅考虑体积,忽略长、 宽、 高等其他因 素)。 同时,要求所有选出的物品中,体积不小于L的物品总数量不能超过C件。

思路:

好像体积大于L的部分可以直接暴力DP而不会超时。然后我们再来考虑体积小于L的部分。
这就好比一个完全背包,然后问你体积W是否可以刚好被填满,但是W太大了,显然不可以直接DP。
我们考虑一个性质,如果 v w ( mod v 1 ) 并且 v w 那么如果v体积可以被表示出来,w也一定可以被表示出来。于是我们可以把体积按照 v 1 的剩余类来划分,同时DP出可以满足每个剩余类的最小的体积,这样就可以直接和询问比较大小了。
考虑怎么DP,因为每一个物品的数量不限,所以DP过程中状态转移图势必会出现环,发现我们其实要求的就是这个状态转移图的单源最短路,设置一个源点,根据之前的体积大于L的部分向剩余类里面的点连边,然后每一个点可以选择任何一个物品,即从 u 连向 ( u + v [ j ] ) % v [ 1 ] ,边权为 v [ j ] ,然后SPFA即可。

/*===========================
 * Author : ylsoi
 * Problem : jzoj4916
 * Algorithm : DP
 * Time : 2018.6.14
 * ========================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<bitset>
#include<queue>
using namespace std;
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=50+10;
const int maxv=1e4+10;
int n,m,vo[maxn],L,C;
int dp[maxv][40],beg[maxv],cnte,dis[maxv];
struct edge{int to,last,w;}E[maxn*maxv];
void add(int u,int v,int w){
    ++cnte;
    E[cnte].to=v;
    E[cnte].last=beg[u];
    beg[u]=cnte;
    E[cnte].w=w;
}
void init(){
    scanf("%d%d",&n,&m);
    REP(i,1,n)scanf("%d",&vo[i]);
    sort(vo+1,vo+n+1);
    scanf("%d%d",&L,&C);
    REP(i,0,vo[1]-1)REP(j,0,C)
        dp[i][j]=inf;
    dp[0][0]=0;
    REP(i,1,n){
        if(vo[i]<L)continue;
        REP(k,1,C)REP(j,0,vo[1]-1){
            int u=((j-vo[i])%vo[1]+vo[1])%vo[1];
            chkmin(dp[j][k],dp[u][k-1]+vo[i]);
        }
    }
}
void SPFA(){
    REP(i,0,vo[1]-1){
        REP(j,1,n){
            if(vo[j]>=L)break;
            int v=(i+vo[j])%vo[1];
            add(i,v,vo[j]);
        }
    }
    REP(i,0,vo[1]-1){
        dis[i]=inf;
        REP(k,0,C)chkmin(dis[i],dp[i][k]);
    }
    queue<int>qu;
    bool vis[maxv]={0};
    REP(i,0,vo[1]-1)if(dis[i]!=inf)
        qu.push((int)i),vis[i]=1;
    while(qu.size()){
        int u=qu.front();
        qu.pop();
        vis[u]=0;
        MREP(i,u){
            int v=E[i].to;
            if(chkmin(dis[v],dis[u]+E[i].w) && !vis[v]){
                vis[v]=1;
                qu.push(v);
            }
        }
    }
}
int main(){
    init();
    SPFA();
    REP(i,1,m){
        ll u;
        scanf("%lld",&u);
        if(dis[u%vo[1]]==inf){
            puts("No");
            continue;
        }
        if(dis[u%vo[1]]<=u)puts("Yes");
        else puts("No");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80697544