noip 模拟 29 完全背包问题
思路挺新奇的,算法的名字也挺好听的: 同余最短路。
我们令 \(dp_{i,j,v}\) 为选到第 \(i\) 个物品,其中选了 \(j\) 件限制物品,所选物品总体积% \(v_{min}\) 意义下为 \(v\),所选物品的最小实际总体积。
判断时用 \(dp[i][j][w\%v_{min}]\) 与 \(w\) 比较即可。
即 \(dp[i][j][w\%v_{min}]\leqslant w\) 时, 判真。
因为当其余数相等时,若物品所需最小的总体积小于等于所给参数,那剩余部分在累加 \(v_{min}\) 的情况下即可满足情况。
处理时分两部分,一种是 \(limit\) 之前的,一种是 \(limit\) 之后的。
前者跑最短路就好了,后者推线性方程,想明白了其实也没那么难。
代码片段 :
inline void sleve() {
s=v[1]+1;
memset(dp,0x3f,sizeof(dp));
dp[0][0][0]=0;
for(re int i=1;i<lim;i++) {
memset(head,0,sizeof(head)); cnt=0;
for(re int k=0;k<v[1];k++) add(s,k,dp[i-1][0][k]);
for(re int k=0;k<v[1];k++) add(k,(k+v[i])%v[1],v[i]);
spfa(s,dp[i][0]);
}
for(re int k=0;k<v[1];k++)
for(re int j=1;j<=c;j++) dp[lim-1][j][k]=dp[lim-1][0][k];
for(re int i=lim;i<=n;i++) {
for(re int k=0;k<v[1];k++) dp[i][0][k]=dp[i-1][0][k];
for(re int j=1;j<=c;j++) {
for(re int k=0;k<v[1];k++) {
dp[i][j][(k+v[i])%v[1]]=min(dp[i-1][j][(k+v[i])%v[1]],dp[i][j-1][k]+v[i]);
}
}
}
for(re int j=1;j<=c;j++) for(re int k=0;k<v[1];k++) dp[n][j][k]=min(dp[n][j][k],dp[n][j-1][k]);
for(re int i=1,w;i<=m;i++) {
w=read();
if(w>=dp[n][c][w%v[1]]) printf("Yes\n");
else printf("No\n");
}
}