题解 完全背包问题

传送门

我愿称之为DZY loves math III,不管是从思维难度上还是从代码的繁琐程度上
哦这个东西好像其实叫同余最短路

细节很多,没能自己调出来……最终康了题解代码巨久才改出来
首先暴力部分的题解有个值得借鉴的思路

  • bitset优化可行性DP

    \[f[k][i][j] = f[k-1][i][j]\ or f[k][i-1][j-V[k]] \]

    发现这里的对于所有i,V[k]都是一个固定的偏移量,所以可以直接压进bitset里面,转移时移位或一下就行了

然后正解
考虑从倍数/余数方向下手
题解神奇思路:
\(v[i]\)中最小的(其它的也行,但跑起来较慢),用DP处理出其它v[j]构造出\(\%v_{min}\)的各个值的最小总体积
然后如果 \(w \geqslant dp[n][c][w\%v_{min}]\) 就合法,否则不合法
因为我已经处理出了构造出这个余数的最小总体积,那么我在每个余数上加上数倍的\(v_{min}\)就可以构造出所有可行方案
所以体积 \(< dp[n][c][w\%v_{min}]\) 时一定无解
可以联想单调队列优化多重背包的那种思路理解一下

然后考虑如何DP
\(f[i][j][k]\)表示第\(i\)种物品,用了\(j\)的限制容量,余数为\(k\)时的最小总体积
然后转移会成环

  • DP转移成环处理方法(抄来的): 加法就高斯消元或系数递推,最值考虑最短路

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 10010
#define ll long long 
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline ll read() {
	ll ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m, l, c, lim; ll w;
int v[N];

namespace force{
	bool f[N];
	bool dfs(int u, int sum) {
		if (sum==w) return 1;
		if (u>n || sum>w) return 0;
		for (int i=1; i<=min(w/v[u], 1ll*(u>=lim?c:INF)); ++i) 
			if (dfs(u+1, sum+i*v[u])) return 1;
		return 0;
	}
	void solve() {
		while (m--) {
			w=read();
			if (dfs(1, 0)) puts("Yes");
			else puts("No");
		}
		exit(0);
	}
}

namespace task1{
	bool dp[32][500010];
	void solve() {
		int lim=c*v[n];
		dp[0][0]=1;
		for (int i=1; i<=n; ++i) 
			for (int j=1; j<=c; ++j) 
				for (int k=v[i]; k<=lim; ++k) 
					dp[j][k] |= dp[j-1][k-v[i]];
		for (int j=1; j<=c; ++j) 
			for (int k=1; k<=lim; ++k) 
				dp[j][k] |= dp[j-1][k];
		for (int i=1; i<=m; ++i) {
			w=read();
			if (w<=lim && dp[c][w]) puts("Yes");
			else puts("No");
		}
		exit(0);
	}
}

namespace task2{
	int s;
	int head[N], size;
	bool vis[N];
	ll dp[52][32][N];
	struct edge{int to, next; ll val;}e[N<<1];
	inline void add(int s, int t, ll w) {edge* k=&e[++size]; k->to=t; k->val=w; k->next=head[s]; head[s]=size;}
	void spfa(int s, ll* dis) {
		dis[s]=0;
		queue<int> q;
		q.push(s);
		int u;
		while (q.size()) {
			u=q.front(); q.pop();
			vis[u]=0;
			for (int i=head[u],v; i; i=e[i].next) {
				v = e[i].to;
				if (dis[v] > dis[u]+e[i].val) {
					dis[v] = dis[u]+e[i].val;
					if (!vis[v]) q.push(v), vis[v]=1;
				}
			}
		}
		//cout<<"dis: "; for (int i=0; i<=v[1]+1; ++i) cout<<dis[i]<<' '; cout<<endl;
	}
	void solve() {
		s=v[1]+1;
		memset(dp, 0x3f, sizeof(dp));
		dp[0][0][0]=0;
		for (int i=1; i<lim; ++i) {
			for (int j=0; j<=c; ++j) {
				memset(head, 0, sizeof(head));
				size=0;
				//add(s, v[i]%v[1], v[i]);
				for (int k=0; k<v[1]; ++k) add(s, k, dp[i-1][j][k]);
				for (int k=0; k<v[1]; ++k) add(k, (k+v[i])%v[1], v[i]);
				spfa(s, dp[i][j]);
			}
		}
		//cout<<"dp: "; for (int i=0; i<v[1]; ++i) cout<<dp[lim-1][c][i]<<' '; cout<<endl;
		for (int i=lim; i<=n; ++i) {
			//dp[i-1][0][v[i]%v[1]] = min(dp[i-1][0][v[i]%v[1]], 1ll*v[i]);
			//dp[i-1][1][v[i]%v[1]] = min(dp[i-1][1][v[i]%v[1]], 1ll*v[i]);
			for (int k=0; k<v[1]; ++k) dp[i][0][k]=dp[i-1][0][k];
			for (int j=1; j<=c; ++j) {
				//dp[i][j-1][v[i]%v[1]] = min(dp[i][j-1][v[i]%v[1]], 1ll*v[i]);
				for (int k=0; k<v[1]; ++k) {
					//cout<<"upd: "<<i<<' '<<j<<' '<<v[i]<<' '<<k<<' '<<dp[i][j][k]<<' '<<dp[i-1][j-1][k]<<endl;
					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]);
					//dp[i][j][k] = min(dp[i][j][k], dp[i-1][j][k]);
					//dp[i][j][k] = min(dp[i][j][k], dp[i][j-1][k]);
					//cout<<"upd: "<<i<<' '<<j<<' '<<v[i]<<' '<<k<<' '<<dp[i][j][k]<<' '<<dp[i-1][j-1][k]<<endl;
				}
			}
			//cout<<"dp"<<i<<": "; for (int k=0; k<v[1]; ++k) cout<<dp[i][c][k]<<' '; cout<<endl;
		}
		//cout<<"dp: "; for (int i=0; i<v[1]; ++i) cout<<dp[n][c][i]<<' '; cout<<endl;
		for (int i=1; i<=c; ++i) for (int j=0; j<v[1]; ++j) dp[n][i][j]=min(dp[n][i][j], dp[n][i-1][j]);
		for (int i=1; i<=m; ++i) {
			w=read();
			if (dp[n][c][w%v[1]]<=w) puts("Yes");
			else puts("No");
		}
		exit(0);
	}
}

signed main()
{
	n=read(); m=read();
	for (int i=1; i<=n; ++i) v[i]=read();
	l=read(); c=read();
	sort(v+1, v+n+1);
	lim=lower_bound(v+1, v+n+1, l)-v;
	//force::solve();
	//if (v[1]>=l) task1::solve();
	task2::solve();
	
	return 0;
}
posted @ 2021-08-03 21:34  Administrator-09  阅读(20)  评论(0编辑  收藏  举报