题解 完全背包问题
我愿称之为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;
}