有后效性的背包,最短路,bitset

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

第一行为两个正整数\(n\)\(m\),分别表示物品的种数以及询问的次数。
第二行为\(n\)个正整数\(V_1,V_2,\dots,V_n(V_i \le 10000)\),分别表示这\(n\)种物品的体积。
第三行为两个非负整数 \(L(L \le 20000 )\)\(C(C\le 30)\),表示在选择方案中对大体积物品的数量限制。
接下来\(m\)行,每行一个正整数\(W_i\),表示这次询问中背包的容量。

输出共\(m\)行,每行一个字符串,表示对应询问的答案。
对于每次询问,如果存在一种合法的方案,请输出\(Yes\),否则输出\(No\)

对于\(10\%\)的数据:\(n\le 8,W_i\le 100\)

对于\(30\%\)的数据:\(W_i\le 10000\)

对于\(60\%\)的数据:\(n\le 30,m\le 200\)

对于另外\(10\%\)的数据:\(n=2\)

对于\(100\%\)的数据:\(n\le 50,m\le 100000,W_i\le 10^{18}\)


考虑暴力

\(dp_{i,j,k}\)表前\(i\)个物品总体积为\(k\)选择了\(j\)件大物品的是否合法。

复杂度\(O(n\max W_i c)\)

考虑修补这个想法。设\(V_0\)为最小的体积

  • 如果\(V_0\ge L\),因为可选的物品总体积不会太大,所以考虑直接暴力,复杂度\(O(nc^2\sum V_i)\),用\(\tt{bitset}\)

优化一下就可以过了。

  • 如果\(V_0 < L\),因为\(V_0\)可以无限选,所以我们不妨求出在同余于\(V_0\)情况下的可能,这样就简化了状态。

\(dp_{i,j,k}\)表示前\(i\)个物品选择了\(j\)件大物品选择的总体积同余于\(V_0\)下为\(k\)的最小总体积。

保证了最小总体积,我们就可以判断\(W_i\)是否大于等于\(dp_{n,\forall j\le c,W_i \bmod V_0}\)来看是否合法

考虑转移

\(dp_{i,j,k}=\min(dp_{i,j,(v_0-v_i)\bmod v_0}+v_i,dp_{i-1,j,k}),v_i < L\)

\(dp_{i,j,k}=\min(dp_{i,j-1,(v_0-v_i)\bmod v_0}+v_i,dp_{i-1,j,k}),v_i \ge L\)

发现第一个转移有环,可以建图解决。

源点\(S\)连接所有\(0 \sim v_0-1\)的点,边权为\(dp_{i,j,v_0}\),然后其他点每个点\(v\)\((v+v_i)\bmod v_0\),边权为\(v_i\),然后跑一边最短路就可以了。

发现这个图比较特殊,可以通过寻找性质\(O(n)\)解决。


Code:

  1. #include <cstdio>
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <bitset>
  5. #define ll long long
  6. int n,c,m,v[52],L;ll w;
  7. int min(int x,int y){return x<y?x:y;}
  8. namespace work1
  9. {
  10. std::bitset <1000001> dp[31];
  11. void work()
  12. {
  13. dp[0][0]=1;
  14. for(int i=1;i<=n;i++)
  15. for(int j=1;j<=c;j++)
  16. dp[j]|=dp[j-1]<<v[i];
  17. for(int i=1;i<=m;i++)
  18. {
  19. scanf("%lld",w);
  20. int ans=0;
  21. for(int j=0;j<=n;j++) ans|=dp[j][w];
  22. if(ans) puts("Yes");
  23. else puts("No");
  24. }
  25. }
  26. }
  27. namespace work2
  28. {
  29. const int N=10010;
  30. const int inf=0x3f3f3f3f;
  31. int dp[31][N],used[32][N],mi,id;
  32. #define dec(a,b) (((a-b)%v[1]+v[1])%v[1])
  33. #define to ((now+v[p])%v[1])
  34. void dfs0(int p,int k,int now)
  35. {
  36. if(used[k][now]) return;
  37. used[k][now]=1;
  38. if(mi>dp[k][now]) mi=dp[k][now],id=now;
  39. dfs0(p,k,to);
  40. }
  41. void dfs1(int d,int p,int k,int now,int anc)
  42. {
  43. if(now==anc) return;
  44. dp[k][now]=min(dp[k][now],d+v[p]);
  45. dfs1(dp[k][now],p,k,to,anc);
  46. }
  47. void topo(int p)
  48. {
  49. memset(used,0,sizeof(used));
  50. for(int i=0;i<=c;i++)
  51. for(int j=0;j<v[1];j++)
  52. if(!used[i][j])
  53. {
  54. mi=inf;
  55. dfs0(p,i,j);
  56. dfs1(dp[i][id],p,i,(id+v[p])%v[1],id);
  57. }
  58. }
  59. void work()
  60. {
  61. memset(dp,0x3f,sizeof(dp));
  62. dp[0][0]=0;
  63. for(int i=2;i<=n;i++)
  64. {
  65. if(v[i]<L) topo(i);
  66. else
  67. {
  68. for(int k=0;k<=c;k++)
  69. for(int j=0;j<v[1];j++)
  70. dp[k][j]=min(k?dp[k-1][dec(j,v[i])]+v[i]:inf,dp[k][j]);
  71. }
  72. }
  73. for(int i=1;i<=m;i++)
  74. {
  75. scanf("%lld",&w);
  76. int ans=0;
  77. for(int j=0;j<=c;j++)
  78. if(dp[j][w%v[1]]!=inf)
  79. ans|=dp[j][w%v[1]]<=w;
  80. if(ans) puts("Yes");
  81. else puts("No");
  82. }
  83. }
  84. }
  85. int main()
  86. {
  87. scanf("%d%d",&n,&m);
  88. for(int i=1;i<=n;i++) scanf("%d",v+i);
  89. scanf("%d%d",&L,&c);
  90. std::sort(v+1,v+1+n);
  91. if(v[1]>=L) work1::work();
  92. else work2::work();
  93. return 0;
  94. }

2018.10.30

版权声明:本文为butterflydew原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/butterflydew/p/9879223.html