背包

 

 

 

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <iostream>
  5 #include <algorithm>
  6 const int MAXN = 100;
  7 const int MAXV = 200000;
  8 using namespace std;
  9 
 10 int n, V;
 11 int num[MAXN + 10], volume[MAXN + 10], value[MAXN + 10];//
 12 
 13 long long f_arr[MAXV + 10];//数组而已,只不过用指针来表示 这个是完全背包滚动数组 
 14 long long g_arr[MAXV + 10];// 
 15 long long *f = f_arr;//
 16 long long *g = g_arr;//
 17 
 18 int fr_arr[MAXV + 10], nfr_arr[MAXV + 10];//意义不明猜测记录路径 
 19 int *fr = fr_arr;
 20 int *nfr = nfr_arr;
 21 
 22 int q[MAXV + 10];//记录当前物品选择个数的数组 
 23 
 24 int vis[MAXV + 10];//记录是否访问过 
 25 int Y;//懒标 
 26 
 27 long long ans = 0;//最大价值 
 28 int ans_r[MAXN + 10];//路径 不明如何计算 
 29 
 30 void dp(int l, int r, int s, int t) {
 31     if (r - l == 1)//当只有一种可以枚举 ,不能再找中点 
 32         return;
 33 
 34     int mid = (l + r + 1) >> 1; // r - l = 1, choose r, correctness trick (not used in this prob)洋屁看不懂,大概意思是在r-l=1的情况下选择r,是一个技巧(但在这段代码里没有使用) 
 35 
 36     f[s] = 0, fr[s] = -1, vis[s] = ++Y;//vis懒标  <-- 
 37     for (int i = l+1; i <= r; ++i) {//枚举第一个物品 
 38         for (int j = 0; j < volume[i]; ++j) {//当前容积 
 39             if (s + j > t) //超出当前背包容积 
 40                 break;
 41             int maxk = (t - (s + j)) / volume[i]; 
 42             int ht = 1, qt = 0;//头和尾存当前这个物品放的个数 
 43             int pos = s + j;//放了当前物品的体积 
 44             for (int k = 0; k <= maxk; ++k) {//完全背包模板 
 45                 while (ht <= qt && k - q[ht] > num[i])
 46                     ++ht;
 47 
 48                 if (vis[pos] == Y) {//如果作为起点访问过  当前q栈里存的方案不够好弹出 
 49                     while (ht <= qt && f[q[qt] * volume[i] + j + s] + (long long) value[i] * (k - q[qt]) <= f[pos])
 50                         --qt;
 51 
 52                     q[++qt] = k;//选k个,把较差的情况从q栈中弹出 
 53                 }
 54 
 55                 if (ht <= qt) {//f和g互换 滚动数组 
 56                     g[pos] = f[q[ht] * volume[i] + j + s] + (long long) value[i] * (k - q[ht]);//当前q栈中存的一定是最优的情况 
 57 
 58                     if (i == mid) nfr[pos] = pos;//路径中点,即(n/2,x) 所以这两个fr nfr是拿来存中点的?? <--
 59                     else nfr[pos] = fr[q[ht] * volume[i] + j + s]; 
 60 
 61                     vis[pos] = Y;//懒标,表示现在这个位置来过 
 62                 }
 63                 else 
 64                     g[pos] = 0, nfr[pos] = -1;//当前情况就是q栈中没有数据,即没有更好的方案,但清零并不能理解 
 65 
 66                 pos += volume[i];//当前背包体积加上当前的物品体积进行下一轮计算 
 67             }
 68         }
 69         swap(f, g);//数组滚动 
 70         swap(fr, nfr);
 71     }
 72     
 73     ans_r[mid] = fr[t];//<--记录中点 
 74 
 75     if (l == 0 && r == n) // first dp, save the ans  ans记录完全背包的最大值 
 76         ans = f[t];
 77 
 78     dp(l, mid, s, ans_r[mid]);//开分 
 79     dp(mid, r, ans_r[mid], t); 
 80 }
 81  
 82 int main() {
 83     freopen("knapsack.in", "r", stdin);
 84     freopen("knapsack.out", "w", stdout); 
 85     cin >> n >> V;
 86     for (int i = 1; i <= n; ++i)
 87         cin >> num[i] >> volume[i] >> value[i];//数量 体积 价值 
 88 
 89     reverse(num + 1, num + n + 1);//反转,其实可以从后往前读入 
 90     reverse(volume + 1, volume + n + 1);
 91     reverse(value + 1, value + n + 1);
 92 
 93     ans_r[0] = 0, ans_r[n] = V;//初始化?? 
 94     dp(0, n, 0, V);//dp入口 
 95 
 96     cout << ans << endl;//最大价值 
 97     for (int i = n; i >= 1; --i) {
 98         cout << (ans_r[i] - ans_r[i-1]) / volume[i];//输出路径开始意义不明 
 99 
100         ans -= (ans_r[i] - ans_r[i-1]) / volume[i] * value[i];//ans到现在没什么软用了吧,可能检验用的 
101 
102         if (i != 1)
103             cout << " ";//输出格式 
104         else
105             cout << endl;
106     }
107 }
View Code

原文地址:https://www.cnblogs.com/lifeisabadword/p/11815576.html