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");
		}
	}
posted @ 2021-08-08 20:50  zJx-Lm  阅读(47)  评论(0编辑  收藏  举报